linq 如何在SelectMany中使用异步lambda?

niwlg2el  于 2022-12-15  发布在  其他
关注(0)|答案(4)|浏览(158)

当我尝试在IEnumerable.SelectMany中使用async lambda时,我得到了以下错误:

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));

有什么办法吗?

yvgpqqbh

yvgpqqbh1#

这是一个扩展:

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压缩结果。

wlzqhblo

wlzqhblo2#

异步lambda表达式无法转换为简单Func<TSource, TResult>
因此,无法使用select many。您可以在同步上下文中运行:

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
    }

}

7eumitmz

7eumitmz3#

Select工作是因为它将返回IEnumerable<Task<T>>,然后可以使用例如Task.WhenAll来等待该IEnumerable<Task<T>>
因此,解决此问题的简单方法是:

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

cxfofazt4#

使用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;
    }
}

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

相关问题