W jaki sposób Task <int> staje się int?


116

Mamy tę metodę:

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

Czy niejawna konwersja występuje między Task<int>a int? Jeśli nie, to co się dzieje? Jak to działa?


1
Czytaj dalej . Zakładam, że kompilator zajmie się tym na podstawie asyncsłowa kluczowego.
— D Stanley

1
@Freeman, spójrz na to świetne wyjaśnienie: stackoverflow.com/a/4047607/280758
— qehgt

Odpowiedzi:


171

Czy niejawna konwersja występuje między Task <> i int?

Nie. To tylko część tego, jak async/ awaitdziała.

Każda metoda zadeklarowana jako asyncmusi mieć zwracany typ:

  • void (unikaj, jeśli to możliwe)
  • Task (brak wyniku poza powiadomieniem o zakończeniu / niepowodzeniu)
  • Task<T>(dla logicznego wyniku typu Tw sposób asynchroniczny)

Kompilator wykonuje wszystkie odpowiednie opakowania. Chodzi o to, że jesteś asynchronicznie powrocie urlContents.Length- nie może sprawić, że metoda po prostu wrócić int, jak rzeczywista metoda zwróci gdy trafi pierwszy awaitwyraz, który nie został już ukończony. Zamiast tego zwraca wartość, Task<int>która zakończy się po zakończeniu samej metody asynchronicznej.

Zauważ, że awaitrobi to odwrotnie - rozpakowuje a Task<T>do Twartości, tak jak działa ta linia:

string urlContents = await getStringTask;

... ale oczywiście rozpakowuje je asynchronicznie, podczas gdy samo użycie Resultspowoduje zablokowanie do zakończenia zadania. ( awaitmoże rozpakować inne typy, które implementują oczekiwany wzorzec, ale Task<T>jest to ten, którego prawdopodobnie będziesz używać najczęściej).

To podwójne zawijanie / rozpakowywanie umożliwia komponowanie asynchronii. Na przykład mógłbym napisać inną metodę asynchroniczną, która wywoła Twoją i podwaja wynik:

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

(Lub po prostu return await AccessTheWebAsync() * 2;oczywiście.)


3
czy można podać jakieś szczegóły dotyczące tego, jak to działa pod maską, po prostu ciekawy.
— Freeman

8
+1 Dobra odpowiedź, jak zawsze. A dlaczego tak szybko je piszesz ?!
— Felix K.

9
+1: Właśnie zacząłem przeglądać async/ awaiti uważam, że jest to wyjątkowo nieintuicyjne. IMO, powinno być słowo kluczowe lub coś podobnego w returncelu wyjaśnienia, np. return async result;(W ten sam sposób, w jaki await result„rozpina się” Tz Tast<T>).
— dav_i

2
@JonSkeet Ale to nie ma sensu bez await- gdy T foo = someTaskT;dostaniesz "Nie można niejawnie przekonwertować typu Task<T>na T" - w ten sam sposób, w jaki twierdzę, że bardziej sensowne byłoby posiadanie słowa kluczowego dla odwrotności (zawijanie Task<T>). Jestem za usuwaniem puchu, ale w tym przypadku wydaje mi się, że zapewnia to niepotrzebne zaciemnianie asyncmetod. (Oczywiście kwestia jest dyskusyjna, ponieważ uprawnienia, które już zostały wypowiedziane / zakodowane!)
— dav_i

2
@dav_i: To zadanie nie ma sensu, ale reszta ma. Są też przypadki, w których całe stwierdzenie miałoby sens - chociaż może nie być przydatne. Biorąc pod uwagę, że metoda jest już zadeklarowana async, myślę, że to wystarczy.
— Jon Skeet

18

Nie wymaga konwersji zadania na int. Po prostu użyj wyniku zadania.

int taskResult = AccessTheWebAndDouble().Result;

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

Zwróci wartość, jeśli jest dostępna, w przeciwnym razie zwróci 0.


20
nie o to prosiłem.
— Freeman,

16
To nie odpowiada na pytanie. Ale co ważniejsze, jest to bardzo zła rada . Prawie nigdy nie powinieneś używać Result; może to spowodować zakleszczenie! Rozważmy na przykład następujący przebieg pracy: (1) Napisz notatkę o treści „skoś trawnik”. (2) Poczekaj, aż trawnik zostanie skoszony (3) Zjedz kanapkę, (4) Zrób wszystko, co jest napisane w notatce. ”Przy takim przepływie pracy nigdy nie jesz kanapki ani nie kosisz trawnika, ponieważ krok 2 to synchroniczne oczekiwanie na temat czegoś, co będziesz robić w przyszłości . Ale to właśnie tutaj opisujesz przepływ pracy.
— Eric Lippert

@EricLippert: Twój przykład nie jest jasny. Czy możesz wyjaśnić, w jaki sposób wynik może wprowadzać zakleszczenia, gdy czekanie nie będzie?
— CharithJ

3
Oczekiwanie oznacza zrobienie czegoś, czekając na wynik i to może obejmować wykonanie pracy w celu obliczenia wyniku. Ale synchroniczne oczekiwania nic nie robią podczas czekania, co oznacza, że ​​możesz uniemożliwić wykonanie pracy.
— Eric Lippert

1
@EricLippert. Czy będzie to miało ten sam problem? 'Task.Run (() => AccessTheWebAndDouble ()). Wynik;'
— CharithJ
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.