正在调用IEnumerable.Select中的异步方法

本文关键字:异步方法 Select IEnumerable 调用 | 更新日期: 2023-09-27 17:59:48

我有以下代码,使用异步方法在类型RL之间转换项:

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,TaskTask<T>,它们都不能转换为'System.Func<R,int,L>'。"

我相信我遗漏了一些关于async/await关键字的内容,但我不知道是什么。有人知道我如何修改代码以使其工作吗?

正在调用IEnumerable.Select中的异步方法

您可以通过考虑游戏中的类型来解决这个问题。例如,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;
  ...
}