継承不要(ダックタイピング)。
成果物
情報源
コード
1
using System; using System.Threading.Tasks; // Task namespace AsyncForeach { class Program { static async Task Main(string[] args) { await foreach (var x in new A()) { Console.WriteLine($"{x}"); } } struct A { // Enumerable必須 public A GetAsyncEnumerator() => this; // Enumerator必須 public int Current => 0; public ValueTask<bool> MoveNextAsync() { Console.WriteLine("MoveNextAsync"); return new ValueTask<bool>(false); } // 未実装なら呼ばれない public ValueTask DisposeAsync() { Console.WriteLine("DisposeAsync"); return default; } } } }
以下の要素が実装されていればいい。そのために何かを継承する必要はない。継承不要のことをパターンベースと呼んでいるっぽい。別サイトではダックタイピングと言っていた。謎の用語が飛び交う魔界。
GetAsyncEnumerator
Current
MoveNextAsync
以下が実装されていればasync using(var x = new A()) {}
で呼ばれる。ただし実装は必須ではなく任意。なくてもいい。
DisposeAsync
実行結果
$ dotnet run MoveNextAsync DisposeAsync
あれ? foreach
のvar x
には何も入っていないのか? false
が返ってくるように見えたのだが……。
2
using System; using System.Threading.Tasks; // Task using System.Threading; // CancellationToken namespace AsyncForeach { class Program { static async Task Main(string[] args) { await foreach (var x in new B()) { Console.WriteLine($"{x}"); } } struct B { // 可変長引数可 public B GetAsyncEnumerator(params int[] dummy) => this; public int Current => 0; // オプション引数可 public ValueTask<bool> MoveNextAsync(CancellationToken token = default) => default; } } }
同期版の各メソッドと違う点として以下がある。
- 可変長引数が使える
- オプション引数が使える
以下参考。
- CancellationToken
- タスクが取り消されたことが原因でタスクが完了していない場合、TaskFactory.ContinueWhenAllメソッドを呼び出すと例外がスローされる
- TaskとValueTaskの使い分け
所感
どういうときに、どう使えるのか、わからなかった。だってループなのに何も出てないじゃん。
対象環境
- Raspbierry pi 3 Model B+
- Raspbian stretch 9.0 2018-11-13 ※
- bash 4.4.12(1)-release ※
- SQLite 3.29.0 ※
- C# dotnet 3.0.100
$ uname -a Linux raspberrypi 4.19.42-v7+ #1218 SMP Tue May 14 00:48:17 BST 2019 armv7l GNU/Linux