2013-02-18 21 views
23

Mam następujący kod, przeliczenia pozycji pomiędzy rodzajami R i L stosując metodę transmisji asynchronicznej:Wywołanie metody asynchronicznej w IEnumerable.Select

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); 
} 

Czy to możliwe, aby napisać używając połączenia IEnumerable.Select (lub podobny), aby zmniejszyć linie kodu? Próbowałem to:

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 

     ... 
    } 
} 

ale pojawia się błąd:

"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>' ."

Wierzę, że jestem brakuje czegoś o async/czekają na słowa kluczowe, ale nie mogę dowiedzieć się, co. Czy jakikolwiek organ wie, w jaki sposób mogę zmodyfikować mój kod, aby działał?

+0

powinny działać, spróbuj nie określając parametrów typu? –

+0

@ofstream: Nie, to nie powinno działać. Komunikat o błędzie jest dość specyficzny. –

+0

Jeśli nie określę parametrów typu ('remoteItems.Select (async r => czeka na MapToLocalObject (r)). ToList()') Otrzymuję 'List >>', który nie jest tym, czego chcę. – PKeno

Odpowiedz

49

Możesz to rozwiązać, biorąc pod uwagę typy w grze. Na przykład: MapToLocalObject - w trybie asynchronicznym - mapuje z R na L. Ale jeśli widzisz to jako funkcję synchroniczną, mapuje ona od R do Task<L>.

Task jest „przyszłość”, więc Task<L> można traktować jako rodzaj że będzie wytworzenia L w pewnym momencie w przyszłości.

Więc można łatwo przekonwertować z sekwencji R do sekwencji Task<L>:

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

Należy pamiętać, że istnieje istotna różnica między tym semantyczny i oryginalnego kodu. Twój oryginalny kod czeka na zmapowanie każdego obiektu przed przejściem do następnego obiektu; ten kod rozpocznie wszystkie odwzorowania jednocześnie.

Twój wynik to sekwencja zadań - sekwencja przyszłych wyników L. Aby pracować z sekwencjami zadań, istnieje kilka typowych operacji. Task.WhenAll i Task.WhenAny są wbudowanymi funkcjami spełniającymi najczęstsze wymagania. Jeśli chcesz czekać, aż wszystkie mapowania zostały zakończone, można zrobić:

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

Jeśli wolisz, aby obsłużyć każdą pozycję jako jej zakończeniem, można użyć OrderByCompletion z my AsyncEx library:

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

To działa. Dziękuję Ci! – PKeno

+0

Chciałbym wiedzieć, jaki jest efekt dodania ".ToArray() "na koniec remoteItems.Select (remoteItem => MapToLocalObject (remoteItem)) będzie. Czy nie umożliwiłoby mi dostępu do wyników zadań, które zostały zakończone, jeśli zewnętrzne zadanie zostanie anulowane? Przepraszam, jeśli to jest nie jest jasne! –

+0

Jeśli powtórzysz sekwencję zadań w zbiorze, uzyskasz dostęp do wyników tych zadań, np. w celu uzyskania częściowego wyniku w przypadku anulowania. –

Powiązane problemy