async void
это костыль .NET’аМногие .NET разработчики знают, что с 5й версии C# в языке появились инструменты для реализации концепции асинхронного программирования.
Ключевые слова
async
/await
, типы данных Task
и Task<T>
, библиотека TPL
.Однако, мало кто знает почему два слова,
async void
, можно написать друг за другом, а компилятор не отругается, в отличие от бородатого сеньора-помидора на код ревью.Казалось бы, всё просто: если метод возвращает что-то типа
T
, то его асинхронный вариант вернёт Task<T>
. Если не возвращается ничего, то есть стоит
void
, то асинхронный вариант имеет тип Task
.Однако, при внедрении киллер фичи, разрабам сишарпа было важно не поломать обратную совместимость и консистентность парадигм. Да-да, C# - это мультипарадигмальный язык программирования. И помимо всего прочего там на уровне языка встроена событийная система.
То есть, можно кидать события, создавать обработчики, регистрировать их, реализовать паттерн Publisher-Subscriber и многое другое. Но, для событийно-ориентированного программирования странно, когда обработчик имеет возвращаемый тип.
Поэтому было принято решение сделать такую штуку, с которой мы живём до сих пор. Всё ради существования асинхронной обработки событий на уровне языка.
Почему
async void
нельзя применять в реализации типовых задач асинхронного программирования?У него совершенно другая семантика.
1️⃣ Исключения, выброшенные в такой среде не отлавливаются через
catch
. Они не оборачиваются в задачу, а вызываются прямо на контексте синхронизации. Отследить такое можно только с помощью глобальных «ловцов всего», что есть прямая дорога к неподдерживаемости.2️⃣ Методы с такой сигнатурой нельзя компоновать с другими асинхронными вызовами через общий API. Цепочки задач, коллбеки при продолжении, общее ожидание - всё это недоступно.
3️⃣ Такой код сложно тестировать и поддерживать. Например, MS Test вовсе работает только с асинхронным кодом, возвращающим
Task
и Task<T>
. Для каждого конкретного случая придётся писать свой собственный контекст синхронизации. Решение не простое и далеко не оптимальное.Ну а больше деталей - на странице MSDN