2013-02-18 8 views
23

Ich habe den folgenden Code, Elemente zwischen den Typen R und L Umwandlung einer Asynchron-Methode:Aufruf asynchrone Methode in 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); 
} 

Ist dies möglich mit einem IEnumerable.Select Anruf (oder ähnliches zu schreiben) um Codezeilen zu reduzieren? Ich versuchte dies:

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 

     ... 
    } 
} 

aber ich bekomme Fehler:

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

Ich glaube, dass ich etwas über das async bin fehlt/erwarten Keywords, aber ich kann nicht herausfinden, was passiert. Weiß jemand, wie ich meinen Code ändern kann, damit er funktioniert?

+0

funktionieren sollte, versuchen nicht die Typparameter angeben? –

+0

@ofstream: Nein, es sollte nicht funktionieren. Die Fehlermeldung ist ziemlich spezifisch. –

+0

Wenn ich keine Typparameter spezifiziere ('remoteItems.Select (async r => erwarte MapToLocalObject (r)). ToList()') Ich bekomme eine 'Liste >>' das ist nicht was ich will. – PKeno

Antwort

49

Sie können dies ausarbeiten, indem Sie die Typen im Spiel berücksichtigen. Beispielsweise wird MapToLocalObject - wenn es als asynchrone Funktion angezeigt wird - von R zu L zugeordnet. Aber wenn Sie es als eine synchrone Funktion betrachten, wird es von R bis Task<L> zugeordnet.

Task ist eine „Zukunft“, so Task<L> kann als eine Art gedacht werden, dass wird eine L in Zukunft irgendwann zu produzieren.

So können Sie leicht aus einer Sequenz von R in eine Folge von Task<L> konvertieren:

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

Beachten Sie, dass es ein wichtiger semantischer Unterschied zwischen diesem und Ihrem ursprünglichen Code. Der ursprüngliche Code wartet darauf, dass jedes Objekt zugeordnet wird, bevor mit dem nächsten Objekt fortgefahren wird. Dieser Code startet alle Zuordnungen gleichzeitig.

Ihr Ergebnis ist eine Folge von Aufgaben - eine Folge von zukünftigen L Ergebnissen. Um mit Abfolgen von Aufgaben zu arbeiten, gibt es ein paar allgemeine Operationen. Task.WhenAll und Task.WhenAny sind integrierte Operationen für die häufigsten Anforderungen. Wenn Sie, bis alle Zuordnungen abgeschlossen haben, warten möchten, können Sie tun:

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

Wenn Sie jedes Element zu behandeln bevorzugen, wie es abgeschlossen ist, können Sie OrderByCompletion verwenden von my AsyncEx library:

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

Das funktioniert. Vielen Dank! – PKeno

+0

Ich wäre interessiert zu wissen, was die Wirkung des Hinzufügens ".ToArray() "bis zum Ende von remoteItems.Select (remoteItem => MapToLocalObject (remoteItem)) wäre. Würde es mir nicht ermöglichen, auf die Ergebnisse von abgeschlossenen Aufgaben zuzugreifen, wenn die äußere Aufgabe abgebrochen wird? nicht klar! –

+0

Wenn Sie die Reihenfolge der Aufgaben in einer Sammlung vereinheitlichen, können Sie auf die Ergebnisse dieser Aufgaben zugreifen, z. B. um im Falle einer Stornierung ein Teilergebnis zu erstellen. –

Verwandte Themen