Wie wird Task <int> zu einem int?

116

Wir haben diese Methode:

async Task<int> AccessTheWebAsync()
{ 
    HttpClient client = new HttpClient();

   Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");

   // You can do work here that doesn't rely on the string from GetStringAsync.
   DoIndependentWork();

   string urlContents = await getStringTask;
   //The thing is that this returns an int to a method that has a return type of Task<int>
   return urlContents.Length;
}

Tritt eine implizite Konvertierung zwischen Task<int>und auf int? Wenn nicht, was passiert dann? Wie ist es implementiert, um zu funktionieren?

Freeman
quelle
1
Lesen Sie weiter . Ich gehe davon aus, dass der Compiler sich basierend auf dem asyncSchlüsselwort darum kümmert .
D Stanley
1
@ Freeman, Schauen Sie sich diese großartige Erklärung an: stackoverflow.com/a/4047607/280758
qehgt

Antworten:

171

Tritt eine implizite Konvertierung zwischen Task <> und int auf?

Nee. Dies ist nur ein Teil dessen, wie async/ awaitfunktioniert.

Jede als deklarierte Methode asyncmuss den Rückgabetyp haben:

  • void (wenn möglich vermeiden)
  • Task (kein Ergebnis über die Benachrichtigung über Fertigstellung / Fehler hinaus)
  • Task<T>(für ein logisches Ergebnis des Typs Tauf asynchrone Weise)

Der Compiler führt alle erforderlichen Umbrüche durch. Der Punkt ist, dass Sie asynchron zurückkehren urlContents.Length- Sie können die Methode nicht einfach zurückgeben lassen int, da die eigentliche Methode zurückkehrt, wenn sie auf den ersten awaitAusdruck trifft, der noch nicht abgeschlossen ist. Stattdessen wird a zurückgegeben, Task<int>das abgeschlossen wird, wenn die asynchrone Methode selbst abgeschlossen ist.

Beachten Sie, dass awaitdas Gegenteil der Fall ist - es packt a Task<T>in einen TWert aus. So funktioniert diese Zeile:

string urlContents = await getStringTask;

... aber natürlich wird es asynchron entpackt, während nur die Verwendung Resultblockieren würde, bis die Aufgabe abgeschlossen ist. ( awaitKann andere Typen auspacken, die das erwartete Muster implementieren, wird aber Task<T>wahrscheinlich am häufigsten verwendet.)

Dieses doppelte Ein- und Auspacken ermöglicht es Async, so zusammensetzbar zu sein. Zum Beispiel könnte ich eine andere asynchrone Methode schreiben, die Ihre aufruft und das Ergebnis verdoppelt:

public async Task<int> AccessTheWebAndDoubleAsync()
{
    var task = AccessTheWebAsync();
    int result = await task;
    return result * 2;
}

(Oder einfach return await AccessTheWebAsync() * 2;natürlich.)

Jon Skeet
quelle
3
Können irgendwelche Details angeboten werden, wie es unter der Haube funktioniert, nur neugierig.
Freeman
8
+1 Gute Antwort wie immer. Und warum schreibst du sie so schnell?!
Felix K.
9
+1: Ich habe gerade angefangen, mich mit async/ zu befassen, awaitund finde das äußerst unintuitiv. IMO sollte es ein Schlüsselwort oder ähnliches geben return, um dies zu verdeutlichen, z. B. return async result;(auf die gleiche Weise, await resultwie das Taus dem "ausgepackt" wird Tast<T>).
Dav_i
2
@JonSkeet Aber es macht keinen Sinn ohne das await- mit T foo = someTaskT;"Sie können den Typ nicht implizit konvertieren Task<T>in T" erhalten - auf die gleiche Weise, wie ich behaupte, dass es sinnvoller wäre, ein Schlüsselwort für die Umkehrung zu haben (Einwickeln Task<T>). Ich bin alle dafür, Flusen zu entfernen, aber in diesem Fall denke ich, dass dies eine unnötige Verschleierung innerhalb der asyncMethoden darstellt. (Offensichtlich ist der Punkt strittig, weil die Kräfte, die bereits gesprochen / codiert wurden!)
dav_i
2
@dav_i: Die Zuordnung macht keinen Sinn, der Rest aber. Und es gibt Fälle, in denen die gesamte Aussage sinnvoll wäre - obwohl sie möglicherweise nicht nützlich ist. Angesichts der Tatsache, dass die Methode bereits deklariert ist async, denke ich, dass das ausreicht.
Jon Skeet
18

Nein erfordert die Konvertierung der Aufgabe in int. Verwenden Sie einfach das Aufgabenergebnis.

int taskResult = AccessTheWebAndDouble().Result;

public async Task<int> AccessTheWebAndDouble()
{
    int task = AccessTheWeb();
    return task;
}

Wenn verfügbar, wird der Wert zurückgegeben, andernfalls wird 0 zurückgegeben.

Aniket Sharma
quelle
20
das habe ich nicht gefragt.
Freeman
16
Dies beantwortet die Frage nicht. Aber was noch wichtiger ist, dies ist ein sehr schlechter Rat . Sie sollten fast nie verwenden Result; es kann Deadlocks verursachen! Betrachten Sie zum Beispiel diesen Workflow: (1) Schreiben Sie eine Notiz mit der Aufschrift "Rasen mähen". (2) Warten Sie, bis der Rasen gemäht ist. (3) Essen Sie ein Sandwich. (4) Tun Sie, was auf dem Zettel steht. "Mit diesem Workflow essen Sie niemals ein Sandwich oder mähen den Rasen, da Schritt 2 ein synchrones Warten ist über etwas, das Sie in Zukunft tun werden . Aber das ist der Workflow, den Sie hier beschreiben.
Eric Lippert
@EricLippert: Klare dein Beispiel nicht. Können Sie bitte erklären, wie das Ergebnis Deadlocks einführen kann, wenn es nicht erwartet wird?
CharithJ
3
Warten bedeutet, etwas zu tun, während Sie auf das Ergebnis warten, und dass etwas die Arbeit zur Berechnung des Ergebnisses beinhalten kann. Synchrone Wartezeiten bewirken jedoch nichts, während Sie warten, was bedeutet, dass Sie möglicherweise verhindern, dass die Arbeit ausgeführt wird.
Eric Lippert
1
@EricLippert. Wird dies das gleiche Problem haben? 'Task.Run (() => AccessTheWebAndDouble ()). Ergebnis;'
CharithJ