正在调用IEnumerable.Select中的异步方法
本文关键字:异步方法 Select IEnumerable 调用 | 更新日期: 2023-09-27 17:59:48
我有以下代码,使用异步方法在类型R
和L
之间转换项:
class MyClass<R,L> {
public async Task<bool> MyMethodAsync(List<R> remoteItems) {
...
List<L> mappedItems = new List<L>();
foreach (var remoteItem in remoteItems )
{
mappedItems.Add(await MapToLocalObject(remoteItem));
}
//Do stuff with mapped items
...
}
private async Task<L> MapToLocalObject(R remoteObject);
}
这是否可以使用IEnumerable进行编写。选择调用(或类似调用)来减少代码行数?我试过这个:
class MyClass<R,L> {
public async Task<bool> MyMethodAsync(List<R> remoteItems) {
...
List<L> mappedItems = remoteItems.Select<R, L>(async r => await MapToLocalObject(r)).ToList<L>();
//Do stuff with mapped items
...
}
}
但我得到错误:
"无法将异步lambda表达式转换为委托类型
'System.Func<R,int,L>'
。异步lambda表达式可以返回CCD_ 4,Task
或Task<T>
,它们都不能转换为'System.Func<R,int,L>'
。"
我相信我遗漏了一些关于async/await关键字的内容,但我不知道是什么。有人知道我如何修改代码以使其工作吗?
您可以通过考虑游戏中的类型来解决这个问题。例如,MapToLocalObject
——当被视为异步函数时——确实从R
映射到L
。但如果您将其视为同步函数,则它会从R
映射到Task<L>
。
Task
是一个"未来",因此Task<L>
可以被认为是将在未来的某个时刻产生L
的类型。
因此,您可以轻松地从R
序列转换为Task<L>
:序列
IEnumerable<Task<L>> mappingTasks = remoteItems.Select(remoteItem => MapToLocalObject(remoteItem));
请注意,这与您的原始代码之间有一个重要的语义差异。您的原始代码等待每个对象被映射,然后再继续到下一个对象;此代码将同时启动所有映射。
您的结果是一系列任务——一系列未来的L
结果。要处理任务序列,有一些常见的操作。CCD_ 19和CCD_ 20是针对最常见需求的内置操作。如果你想等到所有映射都完成,你可以这样做:
L[] mappedItems = await Task.WhenAll(mappingTasks);
如果你喜欢在完成每个项目时处理它,你可以使用我的AsyncEx库中的OrderByCompletion
:
Task<L>[] orderedMappingTasks = mappingTasks.OrderByCompletion();
foreach (var task in orderedMappingTasks)
{
var mappedItem = await task;
...
}