如何在SelectMany中使用async lambda?

Cod*_*gue 19 c# linq lambda linq-to-objects

我在尝试使用asynclambda 时遇到以下错误IEnumerable.SelectMany:

var result = myEnumerable.SelectMany(async (c) => await Functions.GetDataAsync(c.Id));
Run Code Online (Sandbox Code Playgroud)

无法从用法推断出方法'IEnumerable System.Linq.Enumerable.SelectMany(此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();
    }
}
Run Code Online (Sandbox Code Playgroud)

我想因为我的GetDataAsync方法实际上返回了一个Task<IEnumerable<T>>.但为什么Select工作,肯定它应该抛出相同的错误?

var result = myEnumerable.Select(async (c) => await Functions.GetDataAsync(c.Id));
Run Code Online (Sandbox Code Playgroud)

有没有办法解决?

Ser*_*kiy 37

这是一个扩展:

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);
}
Run Code Online (Sandbox Code Playgroud)

这允许你运行:

var result = await myEnumerable.SelectManyAsync(c => Functions.GetDataAsync(c.Id));
Run Code Online (Sandbox Code Playgroud)

说明:您有一个任务列表,每个返回Task<IEnumerable<T>>.所以你需要解雇所有,然后等待所有,然后通过SelectMany压缩结果.

  • 请注意,这会并行化对OP的"GetDataAsync(...)"的所有调用.通常这是一件好事,但在某些情况下可能并不需要. (5认同)
  • 将其提取到方法中而不是将其内联到您的代码中的一个优点是扩展返回一个 `Task&lt;T&gt;`,因此可以在代码中稍后的位置等待它。 (2认同)

pwa*_*was 16

async lambda表达式无法转换为简单Func<TSource, TResult>.

所以,选择很多都不能使用.您可以在同步上下文中运行:

myEnumerable.Select(c => Functions.GetDataAsync(c.Id)).SelectMany(task => task.Result);
Run Code Online (Sandbox Code Playgroud)

要么

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

foreach (var ele in myEnumerable)
{
    result.AddRange(await Functions.GetDataAsyncDo(ele.Id));
}
Run Code Online (Sandbox Code Playgroud)

你不能使用yield return它 - 它是设计的.FE:

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
    }

}
Run Code Online (Sandbox Code Playgroud)

  • Downvote可能是因为使用了`.Result`,这可能会导致死锁(https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html). (2认同)

And*_*ren 6

Select有效,因为它会返回一个IEnumerable<Task<T>>,然后可以用 eg 等待它Task.WhenAll

因此,解决此问题的简单方法是:

IEnumerable<Task<IEnumerable<T>>> tasks = source.Select(GetNestedEnumerableTask);
IEnumerable<T>[] nestedResults = await Task.WhenAll(tasks);
IEnumerable<T> results = nestedResults.SelectMany(nr => nr);
Run Code Online (Sandbox Code Playgroud)


3dG*_*ber 5

使用 C#8,IAsyncEnumerable我们可以更自然地编写:

public static async IAsyncEnumerable<R> SelectManyAsync<T, R>(this IEnumerable<T> ts, 
                                                    Func<T, Task<IEnumerable<R>>> func)
{
    foreach (var t in ts)
    {
        var rs = await func(t);
        foreach (var r in rs)
            yield return r;
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:用于await foreach(...迭代IAsyncEnumerable