web-dev-qa-db-ja.com

SelectManyで非同期ラムダを使用するにはどうすればよいですか?

IEnumerable.SelectMany内でasyncラムダを使用しようとすると、次のエラーが発生します。

var result = myEnumerable.SelectMany(async (c) => await Functions.GetDataAsync(c.Id));

メソッド 'IEnumerable System.Linq.Enumerable.SelectMany(this IEnumerable、Func>)'の型引数は、使用法から推測できません。型引数を明示的に指定してみてください

ここで、GetDataAsyncは次のように定義されています。

public interface IFunctions {
    Task<IEnumerable<DataItem>> GetDataAsync(string itemId);
}

public class Functions : IFunctions {
    public async Task<IEnumerable<DataItem>> GetDataAsync(string itemId) {
        // return await httpCall();
    }
}

私のGetDataAsyncメソッドが実際にTask<IEnumerable<T>>を返すためだと思います。しかし、なぜSelectが機能するのでしょうか。確かに同じエラーがスローされるはずです。

var result = myEnumerable.Select(async (c) => await Functions.GetDataAsync(c.Id));

これを回避する方法はありますか?

23
CodingIntrigue

非同期ラムダ式は単純なFunc<TSource, TResult>に変換できません。

そのため、使用できない多数を選択します。同期されたコンテキストで実行できます。

myEnumerable.Select(c => Functions.GetDataAsync(c.Id)).SelectMany(task => task.Result);

または

List<DataItem> result = new List<DataItem>();

foreach (var ele in myEnumerable)
{
    result.AddRange(await Functions.GetDataAsyncDo(ele.Id));
}

yield returnを使用することはできません。これは仕様によるものです。例:

public async Task<IEnuemrable<DataItem>> Do() 
{
    ...
    foreach (var ele in await Functions.GetDataAsyncDo(ele.Id)) 
    {
        yield return ele; // compile time error, async method 
                          // cannot be used with yield return
    }

}
15
pwas

これは拡張機能です:

public static async Task<IEnumerable<T1>> SelectManyAsync<T, T1>(this IEnumerable<T> enumeration, Func<T, Task<IEnumerable<T1>>> func)
{
    return (await Task.WhenAll(enumeration.Select(func))).SelectMany(s => s);
}

それを実行することができます:

var result = await myEnumerable.SelectManyAsync(c => Functions.GetDataAsync(c.Id));

説明:タスクのリストがあり、それぞれがTask<IEnumerable<T>>を返します。したがって、それらをすべて起動し、すべて待機してから、SelectManyを介して結果をスカッシュする必要があります。

39
SergeyGrudskiy

SelectIEnumerable<Task<T>>を返すため、機能します。 Task.WhenAll

したがって、この問題の簡単な回避策は次のとおりです。

IEnumerable<Task<IEnumerable<T>>> tasks = source.Select(GetNestedEnumerableTask);
IEnumerable<T>[] nestedResults = await Task.WhenAll(tasks);
IEnumerable<T> results = nestedResults.SelectMany(nr => nr);
4
Andreas Ågren