簡體   English   中英

在IEnumerable.Select中調用異步方法

[英]Calling async method in IEnumerable.Select

我有以下代碼,使用異步方法在類型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.Select調用(或類似)來減少代碼行? 我試過這個:

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>' 。“

我相信我錯過了關於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結果。 要處理任務序列,有一些常見的操作。 Task.WhenAllTask.WhenAny是最常見需求的內置操作。 如果要等到所有映射都完成,您可以執行以下操作:

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

如果你喜歡來處理每一個項目,因為它完成,你可以使用OrderByCompletion我AsyncEx庫

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM