20190211のC#に関する記事は9件です。

オブジェクトをジグザグに動かしてみる

はじめに

スーパーマリオ・ソニック等アクションゲームに代表されるものに、物体・地形が動くものがほぼまちがいなくある。
その中でも基本となる上下に動くものを今回作成すると同時に、2種類の方法を用いて挙動の違いを調べてみる。

方法1 Mathf.Sinメソッド

3座標のうち
・2座標を動かす:円運動
・1座標を動かす:ジグザグ

public class CubeLR : MonoBehaviour {
    float x;                       //三角関数の数値設定
    float speed = 3f;              //スピードの数値設
    float radius = 0.1f;           //半径の設定

    // Update is called once per frame
    void Update () {


       x = radius *Mathf.Sin(Time.time * speed);  //三角関数による動きの設定。

       //X座標のみ三角関数による動きの設定を反映
       transform.position = new Vector3(x+transform.position.x,transform.position.y,transform.position.z);  

    }
}

方法2 PingPongメソッド。

PingPong(Time.time(移動スピード),3(移動距離));

public class test : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        transform.position = new Vector3(Mathf.PingPong(Time.time,3),transform.position.y,transform.position.z);
    }
}

結果

青Cube : PingPong
緑Cube : Mathf.Sin

movie.gif

感想

両方ともほぼ同じ動きになるが、PingPongは速度、距離の設定がとても簡単である。
わざわざMathf.Sin関数を使うメリットは今のところ感じられません。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C#による画像処理の速度についての比較検証

1. はじめに

この記事は、C#による画像処理の速度について比較している記事になります。
C#のフレームワークである「.NET Framework」における「GetPixel」「SetPixel」は一般的に速度が遅いと言われているため、実際に、エッジを検出する画像処理のアルゴリズムで、①と②を比較してみました。
① C# unsafeコード使用のアルゴリズム
② .NET FrameworkのGetPixelとSetPixel使用のアルゴリズム

C# unsafeとは、ざっくりC言語のポインタを使えるようにすることです。
ポインタを使うことで、速いアプリを開発することが可能になります。
C# unsafeはメモリを破壊する恐れがあり推奨されていません。画像処理などの重い処理で速度がでない箇所で
最終的な手段としての使用がお勧めかと思います。

2. 結果

まずは結果からです。unsafeコードの方が早く、約32倍の速度がでました。
測定時間は下記の処理の総時間です。
・画像からPixelデータを取得する
・Pixelデータにエッジ検出用のフィルタをかける
・画像にPixelデータを設定する

②を①と全く同じアルゴリズムにしたら、200倍ほどの速度差になってしまったため、
②については、ある程度速度がでるように調整しています。

① unsafeコード ② .NET Framework
170 ms 5517 ms

※検証対象の画像解像度:フルHD(1920×1080)

① unsafeコード結果画像
1.jpg

② .NET Framework結果画像
2.jpg

おまけ結果画像
今回、エッジを検出するアルゴリズムの精度は気にしていなかったが、そこそこエッジを検出できています。

タイルの線と、輪郭が検出できています!!!
自販機のKIRINの文字も検出できてます!!!
3.jpg

時計の数字が検出できていて、これもなかなかいい感じです!!!
4.jpg

3. 成果物

成果物のソースコードはGitHubにアップしております。ご自由に使用ください。
GitHub:https://github.com/toshinomi/ImageProcessingSpeedComp

開発環境 言語 フレームワーク アプリ種類
Visual Studio 2019 Preview 2 C# .NET Framework 4.6 Windows From

※サードパーティ製品ライブラリは未使用で、C#だけでアルゴリズムを書いているため
 他のバージョンのVisual Studioでも、コンパイルできます。
※今回のアプリは比較的重い処理のため、下記の工夫も入れています。説明は省略させて頂きます。
 ・画像処理部分はタスクの非同期処理で行い、画面が固まらないようにしています。
 ・非同期処理から画面の更新をしています。
 ・非同期処理を画面から停止できるようにしています。

操作説明
1.jpg

「File Select...」ボタン:画像を選択します
「All Clear」ボタン:画面表示全てをクリアします
「Filter Start(Unsafe)」ボタン:①のアルゴリズムを実行します。
「Filter Start(Normal)」ボタン:②のアルゴリズムを実行します。
「Stop」ボタン:①または②実行中の処理を停止させます。
「View」チェックボックス:処理の進捗を表示させます。

4. C# unsafeについて

C#やJava(JVM系)のプログラム言語は、コンピュータのメモリ上の任意の場所に自由にアクセスする手段(ポインタ)の利用が禁止または制限されています。
ポインタは非常に便利な機能である反面、システム開発においてバグの原因になりやすく、大きな弊害になっており、昨今のプログラミング言語はポインタがないものが多くなりました。
ただし、C#に関しては、C言語、C++との相互運用性や、プログラムの実行効率向上のために、ポインタが残っています。

C#でポインタを使用するには、下記の手順が必要になります。ポインタがあるのは大変ありがたいです!!!
・unsafeキーワードで囲む

unsafe
{
    // ポインタなどの処理
}

・コンパイル時に、/unsafe オプションを付ける
 ※Visual Studio 2019 Preview 2 の場合、赤枠で囲っているところにチェックを入れます。
5.jpg

5. アルゴリズム比較

アルゴリズムについては大差ありません。unsafeで囲って、画像のPixelデータのアドレスを取得して
フィルタ処理後に計算値を設定するだけです。これだけで32倍速になるなら試す価値があると思います。

① unsafeコード

FormMain.cs
// unsafeで囲みます
unsafe
{
    for (nIndexHeight = 0; nIndexHeight < nHeightSize; nIndexHeight++)
    {
        for (nIndexWidth = 0; nIndexWidth < nWidthSize; nIndexWidth++)
        {
            // バイト型のポインタで、画像のPixelデータのアドレスを取得しています。
            byte* pPixel = (byte*)bitmapData.Scan0 + nIndexHeight * bitmapData.Stride + nIndexWidth * 4;

            double dCalB = 0.0;
            double dCalG = 0.0;
            double dCalR = 0.0;
            double dCalA = 0.0;
            int nIndexWidthMask;
            int nIndexHightMask;
            int nFilter = 0;

            while (nFilter < m_nFilterMax)
            {
                for (nIndexHightMask = 0; nIndexHightMask < nMasksize; nIndexHightMask++)
                {
                    for (nIndexWidthMask = 0; nIndexWidthMask < nMasksize; nIndexWidthMask++)
                    {
                        if (nIndexWidth + nIndexWidthMask > 0 &&
                            nIndexWidth + nIndexWidthMask < nWidthSize &&
                            nIndexHeight + nIndexHightMask > 0 &&
                            nIndexHeight + nIndexHightMask < nHeightSize)
                        {
                            // フィルタ処理のために、バイト型のポインタで、画像のPixelデータのアドレスを取得しています。
                            byte* pPixel2 = (byte*)bitmapData.Scan0 + (nIndexHeight + nIndexHightMask) * bitmapData.Stride + (nIndexWidth + nIndexWidthMask) * 4;

                            dCalB += pPixel2[0] * m_dMask[nIndexWidthMask, nIndexHightMask];
                            dCalG += pPixel2[1] * m_dMask[nIndexWidthMask, nIndexHightMask];
                            dCalR += pPixel2[2] * m_dMask[nIndexWidthMask, nIndexHightMask];
                            dCalA += pPixel2[3] * m_dMask[nIndexWidthMask, nIndexHightMask];
                        }
                    }
                }
                nFilter++;
            }
            // ポインタなので、SetPixelのようなメソッドを呼ばなくて、そのままフィルタ処理後の値を設定できます。
            pPixel[0] = DoubleToByte(dCalB);
            pPixel[1] = DoubleToByte(dCalG);
            pPixel[2] = DoubleToByte(dCalR);
            pPixel[3] = DoubleToByte(dCalA);
        }
    }
    m_bitmapImageFilter.UnlockBits(bitmapData);
}

② .NET Framework

FormMain.cs
for (nIndexHeight = 0; nIndexHeight < nHeightSize; nIndexHeight++)
{
    for (nIndexWidth = 0; nIndexWidth < nWidthSize; nIndexWidth++)
    {
        byte bytePixelB;
        byte bytePixelG;
        byte bytePixelR;
        byte bytePixelA;

        double dCalB = 0.0;
        double dCalG = 0.0;
        double dCalR = 0.0;
        double dCalA = 0.0;

        int nIndexWidthMask;
        int nIndexHightMask;
        int nFilter = 0;

        while (nFilter < m_nFilterMax)
        {
            for (nIndexHightMask = 0; nIndexHightMask < nMasksize; nIndexHightMask++)
            {
                for (nIndexWidthMask = 0; nIndexWidthMask < nMasksize; nIndexWidthMask++)
                {
                    if (nIndexWidth + nIndexWidthMask > 0 &&
                        nIndexWidth + nIndexWidthMask < nWidthSize &&
                        nIndexHeight + nIndexHightMask > 0 &&
                        nIndexHeight + nIndexHightMask < nHeightSize)
                    {
                        // Pixelデータの取得はフィルタ処理中でなく、事前に取得しています。
                        // ここでGetPixelするとオーバーヘッドが凄すぎて断念しました...
                        byte bytePixel2B = m_pixelData[nIndexWidth + nIndexWidthMask, nIndexHeight + nIndexHightMask, (int)Pixel.B];
                        byte bytePixel2G = m_pixelData[nIndexWidth + nIndexWidthMask, nIndexHeight + nIndexHightMask, (int)Pixel.G];
                        byte bytePixel2R = m_pixelData[nIndexWidth + nIndexWidthMask, nIndexHeight + nIndexHightMask, (int)Pixel.R];
                        byte bytePixel2A = m_pixelData[nIndexWidth + nIndexWidthMask, nIndexHeight + nIndexHightMask, (int)Pixel.A];

                        dCalB += bytePixel2B * m_dMask[nIndexWidthMask, nIndexHightMask];
                        dCalG += bytePixel2G * m_dMask[nIndexWidthMask, nIndexHightMask];
                        dCalR += bytePixel2R * m_dMask[nIndexWidthMask, nIndexHightMask];
                        dCalA += bytePixel2A * m_dMask[nIndexWidthMask, nIndexHightMask];
                    }
                }
            }
            nFilter++;
        }
        bytePixelB = DoubleToByte(dCalB);
        bytePixelG = DoubleToByte(dCalG);
        bytePixelR = DoubleToByte(dCalR);
        bytePixelA = DoubleToByte(dCalA);

        // フィルタ処理後に、SetPixelで画像のPixelデータを設定しています。
        m_bitmapImageFilter.SetPixel(nIndexWidth, nIndexHeight, Color.FromArgb(bytePixelA, bytePixelR, bytePixelG, bytePixelB));
    }
}

6. まとめ

unsafeコードが圧倒的に早く(32倍速)、速度問題に対しての有効な解決手段の一つであることが示せました。
C#はマルチプラットフォームの言語の一つで、Web(ASP.NET)、Android、iOS(Xamarin)、
デスクトップ(.NET Framework)、ゲーム(Unity)などの広範囲に使用されており、
速度問題にあたる可能性が高い言語なので今後も有効な手段を模索していきたいと思います。

以上です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Subscribe内でInputクラスを使ってはいけない

理由

デッドロックを起こします。

実際のコード

Test.cs
Observable.Interval(System.TimeSpan.FromSeconds(1), Scheduler.ThreadPool).Take(10).Subscribe(t => {
  Debug.Log(t);
  if(Input.anyKey) {
    Debug.Log("Push!");
  }
});

出力結果

0
//これ以降何も出力されない
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Subscribe内でInputクラスを使ってはいけないケース

//追記(2019/2/11 17時頃)
設定を変更したところ、問題なく動作しました。
そのため改めて書き直しました。

内容

Observable.Internal内でScheduler.ThreadPoolと書き、
Subscribe内でInputクラスを用いるとデッドロックを起こします。

実際のコード

Test.cs
Observable.Interval(System.TimeSpan.FromSeconds(1), Scheduler.ThreadPool).Take(10).Subscribe(t => {
  Debug.Log(t);
  if(Input.anyKey) {
    Debug.Log("Push!");
  }
});

出力結果

0
//これ以降何も出力されない

補足

Audio系クラスでも同様の事象が起きます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

コンソールアプリケーションにアクティビティインジケータを表示

概要

コンソールアプリケーションにおいて、重たい処理を実行中であることをユーザーに伝えるためのアクティビティインジケータ(ビジーマーク)を表示するプログラムをC#とF#で作成しました。

aaa.gif

一定時間毎に...を表示するためにRx(Observable.Timer)を利用しました。

  • 処理中
  • 処理中.
  • 処理中..
  • 処理中...
  • 処理が終わるまで上記の表示の繰り返し、処理が完了したら下記の表示で上書き
  • 処理完了

C#版

nugetでSystem.Reactiveをインストールしておきます。

using System;
using System.Reactive.Linq;
using System.Threading;
using System.Threading.Tasks;

class Program {

  // アクティビティインジケータを出力する関数
  static void IndicationPrinter(string msg1, string msg2, int i) {
    Console.SetCursorPosition(0, Console.CursorTop);
    if (i < 0) {
      Console.WriteLine(msg2);
    } else {
      var n = i % 4;
      Console.Write($"{msg1}{new string('.', n)}{new string(' ', 3 - n)}");
    }
  }

  // インジケータを出力しながら関数funcをする非同期で実行する関数
  static Task<T> RunWithActivityIndicator<S, T>(string msg1, string msg2, Func<S, T> func, S arg) {

    var sTimer = Observable.Timer(TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(0.2));
    var timerSubscription = sTimer.Subscribe(i =>
      IndicationPrinter(msg1, msg2, (int)i));

    return Task.Run(() => {
      return func(arg);
    }).ContinueWith((Task<T> t) => {
      timerSubscription.Dispose();
      IndicationPrinter(msg1, msg2, -1);
      return t.Result;
    });
  }

  static void Main(string[] args) {

    Console.WriteLine("C# Ver.");
    //Console.CursorVisible = false; //カーソルを消したい場合

    Func<string, string> heavyWeightFunc1 = (string arg) => {
      Thread.Sleep(2500);
      return $"=={arg}==";
    };

    // 非常に時間がかかる関数1 (Func<int>) 
    Func<int> heavyWeightFunc2 = () => {
      Thread.Sleep(1500);
      return 0;
    };

    var msg1 = "処理中";
    var msg2 = "処理完了";

    var task1 = RunWithActivityIndicator(msg1, msg2, heavyWeightFunc1, "hoge");
    Console.WriteLine($"処理結果:{task1.Result}");

    Console.ReadKey();
  }
}

F#版

nugetでFSharp.Control.Reactiveをインストールしておきます。

open System
open System.Reactive.Linq
open FSharp.Control.Reactive
open System.Threading
open System.Threading.Tasks

// アクティビティインジケータを出力する関数
let indicationPrinter msg1 msg2 i = 
  Console.SetCursorPosition(0, Console.CursorTop) |> ignore
  if i < 0 then
    printfn "%s" msg2
  else
    match i%4 with
    | 0 -> ignore( printf "%s" (msg1+"   "))
    | 1 -> ignore( printf "%s" (msg1+".  "))
    | 2 -> ignore( printf "%s" (msg1+".. "))
    | _ -> ignore( printf "%s" (msg1+"..."))

// インジケータを出力しながら関数funcをする非同期で実行する関数
let runWithActivityIndicator (msg1,msg2) (func:'S->'T) arg =

  let indicationPrinter = indicationPrinter msg1 msg2

  let sTimer = Observable.Timer(TimeSpan.FromSeconds(0.),TimeSpan.FromSeconds(0.2))
  let timerSubscription = sTimer.Subscribe( fun i -> int(i) |> indicationPrinter  )

  let task = async { return func arg } |> Async.StartAsTask
  task.ContinueWith( fun (t:Task<'T>) -> 
    timerSubscription.Dispose()
    indicationPrinter -1
    t.Result ) 

[<EntryPoint>]
let main argv =

  printfn "F# Ver."
  //Console.CursorVisible <- false; // カーソルを消したい場合

  // 非常に時間がかかる関数1 (string->string) 
  let heavyWeightFunc1 arg =
    Thread.Sleep(1500)
    "==" + arg + "=="

  // 非常に時間がかかる関数2 (unit->string) 
  let heavyWeightFunc2 () =
    Thread.Sleep(2500)
    0

  // 非常に時間がかかる関数3 (unit->unit) 
  let heavyWeightFunc3 () =
    Thread.Sleep(1000)

  let msg = ("処理中","処理完了")  
  let task1 = runWithActivityIndicator msg heavyWeightFunc1 "hoge"
  printfn "%s" ("処理結果 : " + task1.Result )

  let task2 = runWithActivityIndicator msg heavyWeightFunc2 ()
  printfn "%s" ("処理結果 : " + (task2.Result.ToString()) )

  let task3 = runWithActivityIndicator msg heavyWeightFunc3 ()
  task3.Wait()
  printfn "." 

  Console.ReadKey() |> ignore
  0

参考資料

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Resharperチュートリアル]Live Template/Surround Template(コードスニペット)の使い方/作り方

LiveTemplate / SurroundTemplateとは

要するにコードスニペット。標準のVisual Studioでforと打ってTab押したらfor文の雛型が生成されるやつ。

=>公式情報:ライブテンプレートを使用したソースコードの作成

サラウンドテンプレートは選択範囲をtry-catchブロックで囲ったり、lockで囲ったりする場合に使用。

Visual Studioでカスタムのコードスニペットを作るのはxmlファイルいじったりしてめんどくさいけど、Resharperなら簡単に作れる。

使い方

  • liveTemplateの使用例:自作のテストメソッド作成用テンプレート。"tm"と打ってEnter→テストしたいメソッドを入力。
    liveTemplate.gif

    • このように簡単に雛型を作れることがきちんとテストを書くモチベーションにもなる!
  • surroundTemplateの例:try-catchの例

    • ショートカット無し
    • try-catchで囲みたいブロックを選択→ブラシマークをクリック→[Surround With]→tryをクリック

surroundTemplate.gif

  • try-catchはよく書くのでショートカットを覚えると生産的
    • [Ctrl + W]を数回押してメソッド全体選択→[Ctrl + E, Ctrl + U]でサラウンドテンプレートメニューを開く→[8]でtry-catchを選択 surroundTemplate2.gif

作り方

Live Templates

自分がよく使うテストメソッド生成テンプレートの作り方

  1. [Resharperメニュー]→[Tools]→[Templates Explorer...]を選択

liveTemplate1.JPG

  1. Templates Explorerが開く→[Live Templates]タブの[C#]が選択されている状態で新規作成ボタンを押す liveTemplate2.JPG

3.下図のように編集して[Ctrl + s]で保存

  • Shortcut : スニペットのショートカット
  • Description : スニペット洗濯時の説明文
  • 可変部分はドルマークで囲み、変数とする liveTemplate3.JPG

→これで冒頭のGitのように"tm"と打つとコード補完でテストメソッド用のスニペットが選択できるようになる。

なお既存コードからテンプレートの雛型を作成することもできる。

1.テンプレート化したい範囲を選択
lt1.JPG

2.[Resharper]メニュー→[Tools]→[Create Live Templates from Selection...]を選択
lt2.JPG

3.雛型ができる
lt3.JPG

4.雛型を編集。shortcut, Descriptionを入力し、変数化したい部分をドルマークで囲む。
この例ではクラス名にマクロ[Containing type member name]にして、クラス名が自動で入るようにしている。
lt4.JPG

↓作成したテンプレートはこのように、使える
liveTemplate2.gif

Surround Templatesの作り方

ほぼLiveTemplatesと同様。今回は[C#Tips.ログ]Disposable-usingを使った簡単計測ログ出力方法で紹介した計測ログ出力関数用usingのテンプレートを作成してみる

  1. [Resharperメニュー]→[Tools]→[Templates Explorer...]を選択

liveTemplate1.JPG

  1. Templates Explorerが開く→[Surround Templates]タブを選択、[C#]が選択されている状態で新規作成ボタンを押す surroundTemplate1.JPG

3.デフォルトの\$SELECTION$変数が選択範囲になるものとして編集して[Ctrl + s]で保存
surroundTemplate2.JPG

↓このように使える。

surroundTemplate3.gif

まとめ

これ知っとくと楽やで^^。ほんの些細な定型句でもチマチマテンプレート化していくと、退屈な単純作業が楽しいテンプレート遊びと化す!

それでは良いプログラミングライフをノシ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Resharperチュートリアル.テンプレート]Live Template/Surround Template(コードスニペット)の使い方/作り方

LiveTemplate / SurroundTemplateとは

要するにコードスニペット。標準のVisual Studioでforと打ってTab押したらfor文の雛型が生成されるやつ。

=>公式情報:ライブテンプレートを使用したソースコードの作成

サラウンドテンプレートは選択範囲をtry-catchブロックで囲ったり、lockで囲ったりする場合に使用。

Visual Studioでカスタムのコードスニペットを作るのはxmlファイルいじったりしてめんどくさいけど、Resharperなら簡単に作れる。

使い方

  • liveTemplateの使用例:自作のテストメソッド作成用テンプレート。"tm"と打ってEnter→テストしたいメソッドを入力。
    liveTemplate.gif

    • このように簡単に雛型を作れることがきちんとテストを書くモチベーションにもなる!
  • surroundTemplateの例:try-catchの例

    • ショートカット無し
    • try-catchで囲みたいブロックを選択→ブラシマークをクリック→[Surround With]→tryをクリック

surroundTemplate.gif

  • try-catchはよく書くのでショートカットを覚えると生産的
    • [Ctrl + W]を数回押してメソッド全体選択→[Ctrl + E, Ctrl + U]でサラウンドテンプレートメニューを開く→[8]でtry-catchを選択 surroundTemplate2.gif

作り方

Live Templates

自分がよく使うテストメソッド生成テンプレートの作り方

  1. [Resharperメニュー]→[Tools]→[Templates Explorer...]を選択

liveTemplate1.JPG

  1. Templates Explorerが開く→[Live Templates]タブの[C#]が選択されている状態で新規作成ボタンを押す liveTemplate2.JPG

3.下図のように編集して[Ctrl + s]で保存

  • Shortcut : スニペットのショートカット
  • Description : スニペット洗濯時の説明文
  • 可変部分はドルマークで囲み、変数とする liveTemplate3.JPG

→これで冒頭のGifのように"tm"と打つとコード補完でテストメソッド用のスニペットが選択できるようになる。

なお既存コードからテンプレートの雛型を作成することもできる。

1.テンプレート化したい範囲を選択
lt1.JPG

2.[Resharper]メニュー→[Tools]→[Create Live Templates from Selection...]を選択
lt2.JPG

3.雛型ができる
lt3.JPG

4.雛型を編集。shortcut, Descriptionを入力し、変数化したい部分をドルマークで囲む。
この例ではクラス名にマクロ[Containing type member name]にして、クラス名が自動で入るようにしている。
lt4.JPG

↓作成したテンプレートはこのように、使える
liveTemplate2.gif

Surround Templatesの作り方

ほぼLiveTemplatesと同様。今回は[C#Tips.ログ]Disposable-usingを使った簡単計測ログ出力方法で紹介した計測ログ出力関数用usingのテンプレートを作成してみる

  1. [Resharperメニュー]→[Tools]→[Templates Explorer...]を選択

liveTemplate1.JPG

  1. Templates Explorerが開く→[Surround Templates]タブを選択、[C#]が選択されている状態で新規作成ボタンを押す surroundTemplate1.JPG

3.デフォルトの\$SELECTION$変数が選択範囲になるものとして編集して[Ctrl + s]で保存
surroundTemplate2.JPG

↓このように使える。

surroundTemplate3.gif

まとめ

これ知っとくと楽やで^^。ほんの些細な定型句でもチマチマテンプレート化していくと、退屈な単純作業が楽しいテンプレート遊びと化す!

それでは良いプログラミングライフをノシ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

async/await × メソッドチェーン

概要

async/awaitを使ったメソッドをチェーンにする方法を紹介します。
簡単だった割に記事がなかったので記録として残しておきます。

前提条件

以下のクラスを仮定します。

Test.cs
class Test {
  public async Task Operation() {
    //ここの中にメソッドチェーンを書きます
  }

  //適当に用意
  public Test Start() {
    //適当な処理
    return this;
  }

  //適当に用意
  public Test End() {
    //適当な処理
    return this;
  }

  //適当な非同期処理を用意
  private async Task<Test> HogeAsync() {
    await Fuga();    //適当な処理
    return this;
  }
}

書き方

末尾に非同期処理がある場合

以下のように書きます。馴染みのある方が多いかと。

Test.cs
public async Task Operation() {
  await Start().HogeAsync();
}

冒頭に非同期処理がある場合

以下のように書きます。()で一工夫することが必要です。

Test.cs
public async Task Operation() {
  (await HogeAsync()).End();
}

中間に非同期処理がある場合

以下のように書きます。上記の書き方を組み合わせた感じになります。

Test.cs
public async Task Operation() {
  (await Start().HogeAsync()).End();
}

まとめ

async/awaitが用いられた関数をメソッドチェーンの中に組み込むことで、処理の流れを分かりやすく記述することができました。
その一方で、末尾以外に来る場合には()を使わなければいけないため、少し読みづらくなってしまうのが難点です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

VSCodeで c# のバージョンを変える方法

使用エディタ

visual studio code

問題

visual studio codeで C#6 のままで作業している場合、
例えば以下のプロパティの get ステートメントはエラーになる。

適当なプロパティ例
   public int ID { get => this.id; }
エラー文
Feature 'expression body property accessor' is not available in C# 6. Please use language version 7.0 or greater. [Assembly-CSharp]

要は language version というのを 7.0以上にしなさいということのようだ。

※ ちなみに、この記述のドキュメントはこちら
https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/statements-expressions-operators/expression-bodied-members#property-set-statements

対応方法

[プロジェクト名].csprojの中の <LangVersion>6</LangVersion>7 以上にしてあげればOK。

  <PropertyGroup>
    <LangVersion>7</LangVersion>
  </PropertyGroup>

これでC#7.0の文を書いてもエラーが出なくなった。
ちなみに Unity だと、デフォルトで Assembly-CSharp.csproj というファイル名だと思う。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む