In che modo Task <int> diventa un int?


116

Abbiamo questo metodo:

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

Si verifica una conversione implicita tra Task<int>e int? In caso contrario, cosa sta succedendo? Come viene implementato per funzionare?


1
Continua a leggere . Presumo che il compilatore se ne occupi in base alla asyncparola chiave.
D Stanley

1
@Freeman, un'occhiata a questo grande spiegazione: stackoverflow.com/a/4047607/280758
qehgt

Risposte:


171

Si verifica una conversione implicita tra Task <> e int?

No. Questa è solo una parte di come async/ awaitfunziona.

Qualsiasi metodo dichiarato come asyncdeve avere un tipo restituito di:

  • void (evitare se possibile)
  • Task (nessun risultato oltre la notifica di completamento / fallimento)
  • Task<T>(per un risultato logico di tipo Tin modo asincrono)

Il compilatore esegue tutti i wrapping appropriati. Il punto è che stai restituendo in modo asincronourlContents.Length : non puoi fare in modo che il metodo ritorni semplicemente int, poiché il metodo effettivo tornerà quando raggiunge la prima awaitespressione che non è già stata completata. Quindi, invece, restituisce un Task<int>che verrà completato quando il metodo asincrono stesso sarà completato.

Si noti che awaitfa il contrario - si scarta un Task<T>ad un Tvalore, che è come funziona questa linea:

string urlContents = await getStringTask;

... ma ovviamente lo scarta in modo asincrono, mentre il semplice utilizzo Resultsi bloccherebbe fino al completamento dell'attività . ( awaitpuò scartare altri tipi che implementano il modello di attesa, ma Task<T>è quello che probabilmente utilizzerai più spesso.)

Questo doppio avvolgimento / scartamento è ciò che consente a Async di essere così componibile. Ad esempio, potrei scrivere un altro metodo asincrono che chiama il tuo e raddoppia il risultato:

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

(O semplicemente return await AccessTheWebAsync() * 2;ovviamente.)


3
possono essere offerti dettagli su come funziona sotto il cofano, solo curioso.
Freeman

8
+1 Buona risposta come sempre. E perché le scrivi così velocemente ?!
Felix K.

9
+1: Ho appena iniziato a esaminare async/ awaite lo trovo estremamente non intuitivo. IMO, dovrebbe esserci una parola chiave o simile in returnper renderlo chiaro, ad esempio return async result;(nello stesso modo in cui await result"scartare" il Tda Tast<T>).
dav_i

2
@ JonSkeet Ma non ha senso senza await- con T foo = someTaskT;"Non si può convertire implicitamente il tipo Task<T>in T" - allo stesso modo io sostengo che avrebbe più senso avere una parola chiave per l'inverso (avvolgimento Task<T>). Sono a favore della rimozione della peluria, ma in questo caso penso che fornisca un offuscamento non necessario all'interno dei asyncmetodi. (Ovviamente il punto è controverso perché i poteri che si sono già parlati / codificati!)
dav_i

2
@dav_i: l' assegnazione non ha senso, ma il resto sì. E ci sono casi in cui l'intera affermazione avrebbe senso, anche se potrebbe non essere utile. Dato che il metodo è già dichiarato async, penso che sia sufficiente.
Jon Skeet

18

Non è necessario convertire l'attività in int. Usa semplicemente il risultato dell'attività.

int taskResult = AccessTheWebAndDouble().Result;

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

Restituirà il valore se disponibile altrimenti restituirà 0.


20
non è quello che ho chiesto.
Freeman

16
Questo non risponde alla domanda. Ma ancora più importante, questo è un pessimo consiglio . Non dovresti quasi mai usare Result; può causare deadlock! Considera ad esempio questo flusso di lavoro: (1) Scrivi una nota che dice "falcia il prato". (2) Aspetta che il prato venga falciato (3) Mangia un panino, (4) Fai quello che dice sulla nota ". Con quel flusso di lavoro, non mangi mai un panino o falci il prato, perché il passaggio 2 è un'attesa sincrona su qualcosa che farai in futuro . Ma questo è il flusso di lavoro che stai descrivendo qui.
Eric Lippert

@EricLippert: Non è chiaro il tuo esempio. Puoi per favore spiegare come Result può introdurre deadlock quando wait no?
CharithJ

3
Attendere significa fare qualcosa mentre aspetti il ​​risultato e quel qualcosa può includere il lavoro per calcolare il risultato. Ma le attese sincrone non fanno nulla durante l'attesa, il che significa che potresti impedire il completamento del lavoro.
Eric Lippert

1
@EricLippert. Questo avrà lo stesso problema? "Task.Run (() => AccessTheWebAndDouble ()). Risultato;"
CharithJ
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.