在IEnumerable.Select中调用异步方法 [英] Calling async method in IEnumerable.Select

查看:188
本文介绍了在IEnumerable.Select中调用异步方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have the following code, converting items between the types R and L using an async method:

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.Select调用(或类似方法)来减少代码行?我试过了:

Is this possible to write using an IEnumerable.Select call (or similar) to reduce lines of code? I tried this:

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表达式可能返回voidTaskTask<T>,都不能转换为 'System.Func<R,int,L>'."

"Cannot convert async lambda expression to delegate type 'System.Func<R,int,L>'. An async lambda expression may return void, Task or Task<T>, none of which are convertible to 'System.Func<R,int,L>'."

我相信我缺少有关async/await关键字的内容,但我不知道是什么.有谁知道我如何修改我的代码以使其正常工作?

I believe i am missing something about the async/await keywords, but i cannot figure out what. Does any body know how i can modify my code to make it work?

推荐答案

您可以通过考虑所玩游戏的类型来解决这一问题.例如,MapToLocalObject-当被视为异步函数时-确实从R映射到L.但是,如果将其视为同步功能,则它会从R映射到Task<L>.

You can work this out by considering the types in play. For example, MapToLocalObject - when viewed as an asynchronous function - does map from R to L. But if you view it as a synchronous function, it maps from R to Task<L>.

Task是未来",因此可以将Task<L>视为 在将来的某个时刻产生L的类型.

Task is a "future", so Task<L> can be thought of as a type that will produce an L at some point in the future.

因此您可以轻松地将R序列转换为Task<L>序列:

So you can easily convert from a sequence of R to a sequence of Task<L>:

IEnumerable<Task<L>> mappingTasks = remoteItems.Select(remoteItem => MapToLocalObject(remoteItem));

请注意,此代码与您的原始代码之间存在重要的语义差异.您的原始代码等待每个对象被映射,然后再继续下一个对象.该代码将同时启动所有映射.

Note that there is an important semantic difference between this and your original code. Your original code waits for each object to be mapped before proceeding to the next object; this code will start all mappings concurrently.

您的结果是一系列任务-将来的L结果序列.为了处理任务序列,有一些常见的操作. Task.WhenAllTask.WhenAny是最常见要求的内置操作.如果要等到所有映射都完成后,可以执行以下操作:

Your result is a sequence of tasks - a sequence of future L results. To work with sequences of tasks, there are a few common operations. Task.WhenAll and Task.WhenAny are built-in operations for the most common requirements. If you want to wait until all mappings have completed, you can do:

L[] mappedItems = await Task.WhenAll(mappingTasks);

如果您希望在完成每个项目时都进行处理,则可以使用我的AsyncEx库中的OrderByCompletion:

If you prefer to handle each item as it completes, you can use OrderByCompletion from my AsyncEx library:

Task<L>[] orderedMappingTasks = mappingTasks.OrderByCompletion();
foreach (var task in orderedMappingTasks)
{
  var mappedItem = await task;
  ...
}

这篇关于在IEnumerable.Select中调用异步方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆