Deep C# vol.1 – await foreach

はじめに

C# / .NET を深堀していくシリーズを始めます。
Deepと書きつつも一度に多くを扱わずに、1つあたり数分で理解できる軽快なものを書き連ねてゆく予定です。
第1回は await foreach です。

従来のforeachと何が異なるのか

まずはこのコードをご覧ください。C#8.0から採用された await foreach は従来の foreach と何が異なるでしょうか。
コードを比較しましょう。

列挙対象の型が異なります。そこがポイントです。
そのおかげで列挙処理自体を async/await で実装することが出来るようになりました。
これらのコードには多少のバリエーションはありますが、多くの場合以下のように展開されます。

await asyncEnumerator.MoveNextAsync() の部分が非同期化しました。
これは、従来なら enumerator.MoveNext() が同期処理であるためにブロッキングする可能性があったところを回避できるようになったことを意味しています。

厳密なところではGetAsyncEnumerator()の結果に対するnull判定などが含まれますがここでは割愛します。
その件はこの Deep C# で using/await using の解析をする時まで取っておきます。

yield return との相性

C#7.xまでは yield return を非同期化することはできませんでした。
これは比較的単純な話で yield return を用いるメソッドが IAsyncEnumerable<T> に対応していなかったためです。
C#8.0以降は yield return / async / awaitを混在することが可能になりました。

使いどころ

await foreachの使いどころはすでにいくつか考えられます。

  1. ファイルI/O(FileStreamなど)
  2. ネットワークアクセス(HttpClientなど)
  3. シリアライズ/デシリアライズ(JsonSerializerなど)
  4. データベースアクセス(EF Coreなど)

これらの多くには、すでに実際に末尾に Async と付いたメソッドが追加されています。
C#7.xまでは、このいかにも foreach で記述したいところに非同期処理を挟むことが出来ませんでしたから、これは大変な進歩といえます。
また、上記のものを内包するラッパークラスなども大きな恩恵を受けることと思います。

まとめ

C#1.0から存在したforeachが非同期対応したことの意味は大変大きなものです。
これは非同期処理では使えないC#の構文がほぼなくなったことを意味します。await foreachをしっかりと活用したいですね。
第2回は record / record struct を扱います。ご期待ください。

最近の記事

  • 関連記事
  • おすすめ記事
  • 特集記事

アーカイブ

カテゴリー

PAGE TOP