Problem polega na tym, że używasz nie-ogólnej Taskklasy, która nie ma na celu uzyskania wyniku. Kiedy tworzysz Taskinstancję, przekazując delegata asynchronicznego:
Task myTask = new Task(async () =>
... delegat jest traktowany jako async void. async voidNie jest Task, to nie może być oczekiwany, jego wyjątek nie może być obsługiwane, i to jest źródłem tysięcy pytań złożonych przez sfrustrowanych programistów tutaj w StackOverflow i gdzie indziej. Rozwiązaniem jest użycie Task<TResult>klasy ogólnej , ponieważ chcesz zwrócić wynik, a wynik jest inny Task. Musisz więc stworzyć Task<Task>:
Task<Task> myTask = new Task<Task>(async () =>
Teraz, kiedy jesteś Startzewnętrzny Task<Task>, zostanie on ukończony niemal natychmiast, ponieważ jego zadaniem jest po prostu stworzenie wnętrza Task. Będziesz musiał także poczekać na wnętrze Task. Oto jak można to zrobić:
myTask.Start();
Task myInnerTask = await myTask;
await myInnerTask;
Masz dwie możliwości. Jeśli nie potrzebujesz wyraźnego odniesienia do wnętrza Task, możesz po prostu Task<Task>dwukrotnie poczekać na zewnętrzny :
await await myTask;
... lub możesz użyć wbudowanej metody rozszerzenia, Unwrapktóra łączy zadania zewnętrzne i wewnętrzne w jedno:
await myTask.Unwrap();
To rozpakowywanie odbywa się automatycznie, gdy korzystasz ze znacznie popularniejszej Task.Runmetody, która tworzy gorące zadania, więc Unwrapnie jest obecnie często używana.
Jeśli zdecydujesz, że delegat asynchroniczny musi zwrócić wynik, na przykład a string, powinieneś zadeklarować myTaskzmienną typu Task<Task<string>>.
Uwaga: Nie popieram używania Taskkonstruktorów do tworzenia zimnych zadań. Jako, że praktyka jest ogólnie niezadowolona, z powodów, których tak naprawdę nie wiem, ale prawdopodobnie dlatego, że jest używana tak rzadko, że może potencjalnie zaskoczyć innych nieświadomych użytkowników / opiekunów / recenzentów kodu.
Porady ogólne: Uważaj za każdym razem, gdy podajesz delegata asynchronicznego jako argument metody. Ta metoda powinna idealnie oczekiwać Func<Task>argumentu (co oznacza, że rozumie delegatów asynchronicznych) lub przynajmniej Func<T>argumentu (co oznacza, że przynajmniej wygenerowany Tasknie zostanie zignorowany). W niefortunnym przypadku, że ta metoda akceptuje Action, twój delegat będzie traktowany jako async void. To rzadko jest to, czego chcesz, jeśli w ogóle.