Il problema è che stai usando la Taskclasse non generica , che non ha lo scopo di produrre un risultato. Quindi quando si crea l' Taskistanza passando un delegato asincrono:
Task myTask = new Task(async () =>
... il delegato viene trattato come async void. Un async voidnon è una Task, non può essere atteso, la sua eccezione non può essere gestito, ed è una fonte di migliaia di domande fatte dai programmatori frustrati qui in StackOverflow e altrove. La soluzione è utilizzare la Task<TResult>classe generica , perché si desidera restituire un risultato e il risultato è un altro Task. Quindi devi creare un Task<Task>:
Task<Task> myTask = new Task<Task>(async () =>
Ora quando sei Startesterno Task<Task>sarà completato quasi istantaneamente perché il suo compito è solo quello di creare l'interno Task. Dovrai quindi aspettare anche l'interno Task. Ecco come si può fare:
myTask.Start();
Task myInnerTask = await myTask;
await myInnerTask;
Hai due alternative. Se non hai bisogno di un riferimento esplicito all'interiore Task, puoi semplicemente attendere l'esterno Task<Task>due volte:
await await myTask;
... oppure puoi utilizzare il metodo di estensione incorporato Unwrapche combina le attività esterne e interne in una:
await myTask.Unwrap();
Questo scartamento si verifica automaticamente quando si utilizza il Task.Runmetodo molto più popolare che crea attività a caldo, quindi al Unwrapgiorno d'oggi non viene utilizzato molto spesso.
Nel caso in cui decidi che il tuo delegato asincrono deve restituire un risultato, ad esempio a string, devi dichiarare che la myTaskvariabile è di tipo Task<Task<string>>.
Nota: non approvo l'uso di Taskcostruttori per la creazione di attività a freddo. Dato che una pratica è generalmente disapprovata, per ragioni che non conosco davvero, ma probabilmente perché è usata così raramente che ha il potenziale di sorprendere di sorpresa altri utenti / manutentori / revisori del codice.
Consiglio generale: fare attenzione ogni volta che si fornisce un delegato asincrono come argomento a un metodo. Questo metodo dovrebbe idealmente aspettarsi un Func<Task>argomento (nel senso che comprende i delegati asincroni), o almeno un Func<T>argomento (nel senso che almeno il generato Tasknon verrà ignorato). Nel malaugurato caso in cui questo metodo accetti un Action, il tuo delegato verrà trattato come async void. Questo è raramente quello che vuoi, se mai.