Como Task <int> se torna um int?

116

Temos este método:

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

Uma conversão implícita ocorre entre Task<int>e int? Se não, o que está acontecendo? Como é implementado para funcionar?

Freeman
fonte
1
Continue lendo . Presumo que o compilador cuide disso com base na asyncpalavra - chave.
D Stanley,
1
@Freeman, olhe para esta ótima explicação: stackoverflow.com/a/4047607/280758
qehgt

Respostas:

171

Uma conversão implícita ocorre entre Task <> e int?

Não. Isso é apenas parte de como async/ awaitfunciona.

Qualquer método declarado como asyncdeve ter um tipo de retorno de:

  • void (evite se possível)
  • Task (nenhum resultado além da notificação de conclusão / falha)
  • Task<T>(para um resultado lógico de tipo de Tmaneira assíncrona)

O compilador faz todo o empacotamento apropriado. A questão é que você está retornando de forma assíncronaurlContents.Length - você não pode fazer o método apenas retornar int, pois o método real retornará quando atingir a primeira awaitexpressão que ainda não foi concluída. Em vez disso, ele retorna um Task<int>que será concluído quando o próprio método assíncrono for concluído.

Observe que awaitfaz o oposto - ele desembrulha a Task<T>em um Tvalor, que é como esta linha funciona:

string urlContents = await getStringTask;

... mas é claro que ele desembrulha de forma assíncrona, ao passo que apenas o uso Resultbloquearia até que a tarefa fosse concluída. ( awaitpode desembrulhar outros tipos que implementam o padrão aguardado, mas Task<T>é aquele que você provavelmente usará com mais frequência.)

Esse empacotamento / desembrulhamento duplo é o que permite que o assíncrono seja tão combinável. Por exemplo, eu poderia escrever outro método assíncrono que chama o seu e duplica o resultado:

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

(Ou simplesmente, é return await AccessTheWebAsync() * 2;claro.)

Jon Skeet
fonte
3
algum detalhe pode ser oferecido sobre como funciona nos bastidores, só por curiosidade.
Freeman
8
+1 Boa resposta como sempre. E por que você está escrevendo tão rápido ?!
Felix K.
9
+1: Acabei de começar a pesquisar async/ awaite acho isso extremamente não intuitivo. IMO, deve haver uma palavra-chave ou similar no returnpara deixar isso claro, por exemplo return async result;(da mesma forma que await result"desembrulha" o Tdo Tast<T>).
dav_i
2
@JonSkeet Mas não faz sentido sem o await- com T foo = someTaskT;você obteria "Não é possível converter implicitamente o tipo Task<T>para T" - da mesma forma, eu argumento que faria mais sentido ter uma palavra-chave para o inverso (agrupamento Task<T>). Eu sou totalmente a favor da remoção de cotão, mas, neste caso, acho que isso proporciona uma ofuscação desnecessária nos asyncmétodos. (Obviamente, a questão é discutível porque os poderes que já falaram / codificaram!)
dav_i
2
@dav_i: A atribuição não faz sentido, mas o resto sim. E há casos em que toda a declaração faria sentido - embora possa não ser útil. Dado que o método já está declarado async, acho que é o suficiente.
Jon Skeet
18

Não requer a conversão da Tarefa em int. Basta usar o resultado da tarefa.

int taskResult = AccessTheWebAndDouble().Result;

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

Ele retornará o valor se disponível, caso contrário, retornará 0.

Aniket Sharma
fonte
20
não foi isso que eu perguntei.
Freeman
16
Isso não responde à pergunta. Mas, mais importante, este é um conselho muito ruim . Você quase nunca deve usar Result; pode causar bloqueios! Considere, por exemplo, este fluxo de trabalho: (1) Escreva uma nota que diz "corte a grama". (2) Espere que a grama seja cortada (3) Coma um sanduíche, (4) Faça o que está escrito na nota ". Com esse fluxo de trabalho, você nunca come um sanduíche ou corta a grama, porque a etapa 2 é uma espera sincronizada em algo que você fará no futuro . Mas esse é o fluxo de trabalho que você está descrevendo aqui.
Eric Lippert
@EricLippert: Não esclarece seu exemplo. Você pode explicar como o Result pode introduzir impasses quando o Wait não vai?
CharithJ
3
Esperar significa fazer algo enquanto você espera pelo resultado e esse algo pode incluir trabalhar para calcular o resultado. Mas as esperas síncronas não fazem nada enquanto você espera, o que significa que você pode estar impedindo que o trabalho seja feito.
Eric Lippert
1
@EricLippert. Isso terá o mesmo problema? 'Task.Run (() => AccessTheWebAndDouble ()). Result;'
CharithJ