C#(.net6.0)でFunc<T>
型の生成メソッドを使ってEnumerable
の無限リストを作成するメソッドを定義します。
public static IEnumerable<T> Generate<T>(Func<T> supplier) { while (true) yield return supplier(); }
生成メソッドsupplier
に非同期メソッドを使いたい場合は次のように少し変更が必要です。
public static async IAsyncEnumerable<T> GenerateAsync<T>(Func<Task<T>> supplierAsync) { while (true) yield return await supplierAsync(); }
使用例
StreamReader
で読み込んだテキストファイルをReadLine
を使って行ごとのリストにする処理です。
非同期版のReadLineAsync
でも同様のことをしてみます。
public static class Program { public static IEnumerable<T> Generate<T>(Func<T> supplier) { while (true) yield return supplier(); } public static async IAsyncEnumerable<T> GenerateAsync<T>(Func<Task<T>> supplierAsync) { while (true) yield return await supplierAsync(); } public static void ForEach<T>(this IEnumerable<T> it, Action<T> consumer) { foreach (var item in it) consumer(item); } static async Task Main(string[] args) { // 同期版 using var file1 = new StreamReader("test.txt"); Generate(file1.ReadLine) .TakeWhile(line => line is not null) .ForEach(Console.WriteLine); Console.WriteLine(); // 非同期版 using var file2 = new StreamReader("test.txt"); await GenerateAsync(file2.ReadLineAsync) .TakeWhile(line => line is not null) .ForEachAsync(line => Console.WriteLine(line)); } }
ForEachAsync
は System.Linq.Async -version 6.0.1に入っているものです。
ForEachAsync
で同期版のようにConsole.WriteLine
を直接デリゲートで渡すのではなくラムダ式で渡している理由は、
ForEachAsync
のオーバーロードの関数が次の二つあり、
ForEachAsync<T>(this IAsyncEnumerable<T> source, Action<T> action, CancellationToken cancellationToken = default(CancellationToken)) ForEachAsync<T>(this IAsyncEnumerable<T> source, Action<T, int> action, CancellationToken cancellationToken = default(CancellationToken))
そしてたまたまConsole.WriteLine
のオーバーロード関数にも次の2つがあったため、直接Console.WriteLine
のデリゲートを渡しただけでは関数シグネチャの解決ができなかったためです。
WriteLine(string? value) WriteLine(string format, object? arg0)