Як Завдання <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 Стенлі

1
@Freeman, Подивіться на це велике пояснення: stackoverflow.com/a/4047607/280758
qehgt

Відповіді:


171

Чи відбувається неявна конверсія між Task <> та int?

Ні. Це лише частина того, як async/ awaitпрацює.

Будь-який метод, оголошений як asyncповинен мати тип повернення:

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

Компілятор робить усі необхідні обгортання. Справа в тому, що ви асинхронно повертаєтесь urlContents.Length- ви не можете змусити метод просто повернутися int, оскільки власне метод повернеться, коли він потрапить у перший awaitвираз, який ще не завершився. Отже, замість цього він повертає a, Task<int>який завершиться після завершення самого методу асинхронізації.

Зверніть увагу , що awaitробить протилежне - він розгортаєTask<T> до Tвартості, яка , як працює цей рядок:

string urlContents = await getStringTask;

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

Це подвійне обгортання / розгортання - це те, що дозволяє асинхронізації бути такою композиційною. Наприклад, я можу написати інший метод асинхронізації, який викликає ваш і подвоює результат:

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

(Або просто return await AccessTheWebAsync() * 2;звичайно.)


3
можна запропонувати будь-які деталі про те, як це працює під капотом, просто цікаво.
Фріман

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методів. (Очевидно, суть суперечка тому, що сили, про які вже говорилося / кодувались!)
dav_i

2
@dav_i: Призначення не має сенсу, але все інше. І бувають випадки, коли вся заява мала б сенс - хоча це може бути не корисним. З огляду на те, що метод вже задекларований async, я думаю, що цього достатньо.
Джон Скіт

18

Не потрібно перетворювати Завдання в int. Просто скористайтеся результатом завдання.

int taskResult = AccessTheWebAndDouble().Result;

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

Він поверне значення, якщо воно є, інакше воно поверне 0.


20
це не те, що я запитав.
Фріман

16
Це не дає відповіді на запитання. Але що ще важливіше, це дуже погана порада . Ви майже не повинні використовувати Result; це може призвести до тупиків! Розглянемо для прикладу цей робочий процес: (1) Напишіть примітку, що говорить "косити газон". (2) Зачекайте, коли газон буде скошений (3) Їжте бутерброд, (4) Робіть все, що написано на замітці ". За допомогою цього робочого процесу ви ніколи не їсте бутерброд чи косити газон, тому що крок 2 - це синхронне очікування на щось, що ви будете робити в майбутньому . Але це робочий процес, який ви тут описуєте.
Ерік Ліпперт

@EricLippert: Не зрозуміти свій приклад. Чи можете ви пояснити, як результат може ввести тупикові місця, коли очікування не буде?
CharithJ

3
Очікувати означає зробити щось, поки ви чекаєте результату, і щось може включати в себе роботу для обчислення результату. Але синхронні очікування нічого не роблять, поки ви чекаєте, що означає, що ви можете заважати виконувати роботу.
Ерік Ліпперт

1
@EricLippert. Чи матиме це те саме питання? 'Task.Run (() => AccessTheWebAndDouble ()). Результат;'
CharithJ
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.