Пожалуйста, прочитайте важную заметку внизу.
Метод async void
приведет к аварийному завершению работы приложения, поскольку для компилятора C # нет объекта Task
, в который было бы помещено исключение. На функциональном уровне ключевое слово async
в методе Task
- просто тяжелый синтаксический сахар, который говорит компилятору переписать ваш метод в терминах объекта Task
, используя различные методы, доступные для объекта, а также такие утилиты, как Task.FromResult
, Task.FromException
и Task.FromCancelled
или иногда Task.Run
, или эквиваленты с точки зрения компилятора. Это означает, что код такой:
async Task Except()
{
throw new Exception { };
}
превращается в приблизительно :
Task Except()
{
return Task.FromException(new Exception { });
}
и так, когда вы вызываете Task
-возврат async
методов, которые throw
, программа не падает, потому что на самом деле не выдается исключение; вместо этого объект Task
создается в «исключенном» состоянии и возвращается вызывающей стороне. Как упоминалось ранее, метод, декорированный async void
, не имеет возвращаемого объекта Task
, поэтому компилятор не пытается переписать метод в терминах объекта Task
, а вместо этого пытается только иметь дело с получение значений ожидаемых звонков.
Больше контекста
Task
- методы возврата могут также вызывать исключения, даже если они не ожидаются, потому что ключевое слово async
является причиной проглатывания, поэтому, если оно отсутствует, исключения в методе не будут проглатываться, например, следующим образом.
Task Except() // Take note that there is no async modifier present.
{
throw new Exception { }; // This will now throw no matter what.
return Task.FromResult(0); // Task<T> derives from Task so this is an implicit cast.
}
Причина, по которой ожидание вызова на самом деле будет throw
исключением, предположительно возникающим в Task
-возвратном методе async
, заключается в том, что ключевое слово await
должно выбрасывать проглоченные Exception
с, чтобы сделать отладка проще в асинхронном контексте.
Важное примечание
Способ, которым эти «переписывания» фактически обрабатываются компилятором и проявляется скомпилированным кодом, может отличаться от того, что я подразумевал, но примерно эквивалентен на функциональном уровне.