Как Task <int> становится int?

116

У нас есть такой метод:

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

Происходит ли неявное преобразование между Task<int>и int? Если нет, то что происходит? Как это реализовано для работы?

почетный гражданин
источник
1
Продолжайте читать . Я предполагаю, что компилятор позаботится об этом на основе asyncключевого слова.
D Stanley
1
@Freeman, посмотрите на это отличное объяснение: stackoverflow.com/a/4047607/280758
qehgt

Ответы:

171

Происходит ли неявное преобразование между Task <> и int?

Нет. Это всего лишь часть того, как async/ awaitработает.

Любой метод, объявленный как, asyncдолжен иметь возвращаемый тип:

  • void (по возможности избегайте)
  • Task (нет результата, кроме уведомления о завершении / неудаче)
  • Task<T>(для логического результата типа Tасинхронно)

Компилятор выполняет все необходимые обертывания. Дело в том, что вы возвращаетесь асинхронноurlContents.Length - вы не можете заставить метод просто возвращаться int, поскольку фактический метод вернется, когда он попадет в первое awaitвыражение, которое еще не завершено. Поэтому вместо этого он возвращает, Task<int>который завершится, когда завершится сам асинхронный метод.

Обратите внимание, что awaitпроисходит обратное - он разворачивает a Task<T>в Tзначение, как работает эта строка:

string urlContents = await getStringTask;

... но, конечно, он разворачивает его асинхронно, тогда как простое использование Resultблокирует его до тех пор, пока задача не будет завершена. ( awaitможет разворачивать другие типы, реализующие ожидаемый шаблон, но Task<T>это тот, который вы, вероятно, будете использовать чаще всего.)

Эта двойная упаковка / развертывание - это то, что позволяет async быть настолько компонуемым. Например, я мог бы написать еще один асинхронный метод, который вызывает ваш и удваивает результат:

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

(Или просто return await AccessTheWebAsync() * 2;конечно.)

Джон Скит
источник
3
Могут ли быть предложены какие-либо подробности того, как это работает под капотом, просто любопытно.
Freeman
8
+1 Хороший ответ как всегда. И почему ты так быстро их пишешь ?!
Феликс К.
9
+1: Только начал изучать async/, awaitи я считаю это крайне не интуитивным. ИМО, должно быть ключевое слово или подобное в, returnчтобы прояснить это, например return async result;(так же, как await result"разворачивает" Tиз Tast<T>).
dav_i
2
@JonSkeet Но это не имеет смысла без await- T foo = someTaskT;если бы вы получили «Невозможно неявно преобразовать тип Task<T>в T» - точно так же, как я утверждаю, было бы больше смысла иметь ключевое слово для обратного (обертывания Task<T>). Я полностью за удаление мусора, но в данном случае я думаю, что это обеспечивает ненужную обфускацию внутри asyncметодов. (Очевидно, это спорный вопрос, потому что
сильные мира сего
2
@dav_i: назначение не имеет смысла, но остальное имеет смысл. А бывают случаи, когда вся фраза имеет смысл, хотя может и бесполезно. Учитывая, что метод уже заявлен async, думаю, этого достаточно.
Джон Скит
18

Не требует преобразования Task в int. Просто используйте результат задачи.

int taskResult = AccessTheWebAndDouble().Result;

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

Он вернет значение, если доступно, в противном случае вернет 0.

Аникет Шарма
источник
20
я спросил не об этом.
Freeman
16
Это не отвечает на вопрос. Но что еще более важно, это очень плохой совет . Вы почти никогда не должны использовать Result; это может вызвать взаимоблокировки! Рассмотрим, например, следующий рабочий процесс: (1) Напишите заметку «косить лужайку». (2) Дождитесь, пока газон будет подстрижен (3) Съешьте бутерброд, (4) Делайте все, что написано в записке ". При таком рабочем процессе вы никогда не съедите бутерброд и не косите газон, потому что шаг 2 - это синхронное ожидание. на то, что вы будете делать в будущем . Но это рабочий процесс, который вы описываете здесь.
Эрик Липперт
@EricLippert: Непонятен ваш пример. Не могли бы вы объяснить, как Result может вызывать тупиковые ситуации, когда await не работает?
CharithJ
3
Ожидать означает делать что-то, пока вы ждете результата, и что-то может включать в себя выполнение работы по вычислению результата. Но синхронные ожидания ничего не делают, пока вы ждете, что означает, что вы можете помешать выполнению работы.
Эрик Липперт
1
@EricLippert. Будет ли у этого такая же проблема? 'Task.Run (() => AccessTheWebAndDouble ()). Результат;'
CharithJ