如何将异步 lambda 与 SelectMany 一起使用?
How to use async lambda with SelectMany?
尝试在 IEnumerable.SelectMany
中使用 async
lambda 时出现以下错误:
var result = myEnumerable.SelectMany(async (c) => await Functions.GetDataAsync(c.Id));
The type arguments for method 'IEnumerable
System.Linq.Enumerable.SelectMany(this
IEnumerable, Func>)' cannot be
inferred from the usage. Try specifying the type arguments explicitly
其中 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
方法实际上 returns 一个 Task<IEnumerable<T>>
。但是为什么 Select
工作,它肯定会抛出同样的错误?
var result = myEnumerable.Select(async (c) => await Functions.GetDataAsync(c.Id));
有什么解决办法吗?
异步 lambda 表达式无法转换为简单 Func<TSource, TResult>
.
所以,select很多都不能用。您可以 运行 在同步上下文中:
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
- 这是设计使然。 f.e.:
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
}
}
这是一个扩展:
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));
说明:您有一个任务列表,每个 returns Task<IEnumerable<T>>
。所以你需要将它们全部触发,然后等待所有,然后通过 SelectMany 压缩结果。
Select
之所以有效,是因为它将 return 一个 IEnumerable<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);
使用 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;
}
}
注意:
使用 async foreach(...
迭代 IAsyncEnumerable
尝试在 IEnumerable.SelectMany
中使用 async
lambda 时出现以下错误:
var result = myEnumerable.SelectMany(async (c) => await Functions.GetDataAsync(c.Id));
The type arguments for method 'IEnumerable System.Linq.Enumerable.SelectMany(this IEnumerable, Func>)' cannot be inferred from the usage. Try specifying the type arguments explicitly
其中 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
方法实际上 returns 一个 Task<IEnumerable<T>>
。但是为什么 Select
工作,它肯定会抛出同样的错误?
var result = myEnumerable.Select(async (c) => await Functions.GetDataAsync(c.Id));
有什么解决办法吗?
异步 lambda 表达式无法转换为简单 Func<TSource, TResult>
.
所以,select很多都不能用。您可以 运行 在同步上下文中:
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
- 这是设计使然。 f.e.:
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
}
}
这是一个扩展:
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));
说明:您有一个任务列表,每个 returns Task<IEnumerable<T>>
。所以你需要将它们全部触发,然后等待所有,然后通过 SelectMany 压缩结果。
Select
之所以有效,是因为它将 return 一个 IEnumerable<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);
使用 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;
}
}
注意:
使用 async foreach(...
迭代 IAsyncEnumerable