[英]Calling async method in IEnumerable.Select
我有以下代碼,使用異步方法在類型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.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表達式可能返回void
,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
結果。 要處理任務序列,有一些常見的操作。 Task.WhenAll
和Task.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.