Чи слід уникати обробників подій "async void"?


119

Я знаю, що загалом вважається поганою ідеєю використовувати async voidметоди " забуття та забуття" для запуску завдань, оскільки немає сліду очікуваного завдання, і складно обробляти винятки, які можуть бути кинуті всередині такого методу.

Чи варто взагалі уникати async voidобробників подій? Наприклад,

private async void Form_Load(object sender, System.EventArgs e)
{
        await Task.Delay(2000); // do async work
        // ...
} 

Я можу це переписати так:

Task onFormLoadTask = null; // track the task, can implement cancellation

private void Form_Load(object sender, System.EventArgs e)
{
        this.onFormLoadTask = OnFormLoadTaskAsync(sender, e);
} 

private async Task OnFormLoadTaskAsync(object sender, System.EventArgs e)
{
        await Task.Delay(2000); // do async work
        // ...
} 

Які є підводні скелі для асинхронних обробників подій, окрім можливої ​​повторної участі?


Ти повинен, але не можеш. Крім того, всі турботи, які ви повинні взяти під час використання асинхроністичної порожнечі, вже вимагають обробники подій інтерфейсу користувача.
— Пауло Моргадо

А повторне ставлення відбувається через асинхронні операції, запущені обробником подій, а не через використання асинхрон-очікування самого себе.
— Пауло Моргадо

Відповіді:


153

Настанова полягає в тому, щоб уникати, async void за винятком випадків, коли вони використовуються в обробці подій, тому використання async voidв обробці подій нормально.

Однак, з причин тестування одиниць, я часто люблю визначати логіку всіх async voidметодів. Наприклад,

public async Task OnFormLoadAsync(object sender, EventArgs e)
{
  await Task.Delay(2000);
  ...
}

private async void Form_Load(object sender, EventArgs e)
{
  await OnFormLoadAsync(sender, e);
}

Мені цікаво ... чи є причина, чому ви не просто змінюєте Form_Loadдоступ public? Здається, що такий код буде менш дослідним.
— InteXX

На жаль, VBer намагається прочитати C # тут ... Я щойно помітив тип повернення OnFormLoadAsync. Зараз я бачу, що це сприятливо вигадує. Дякую.
— InteXX

Все, що сказано, ви можете подивитися і запропонувати свою думку тут . Дякую!
— InteXX

2
@ AlexHopeO'Connor: Handledпрапор повинен бути встановлений синхронно; не можна використовувати asyncдля прийняття рішення про те, чи проводиться подія чи ні.
— Стівен Клірі

2
@ AlexHopeO'Connor: Минув час, коли я працював із програмою WPF, але в минулому я використовував рішення, подібні до того, що було. Тобто зробити ICommand.Executeметод async void; Я вважаю це прийнятним , оскільки ICommand.Executeє логічно обробник події.
— Стівен Клірі

50

Чи варто взагалі уникати асинхронних обробників недійсних подій?

Як правило, обробники подій - це один випадок, коли метод асинхронізації, який недійсний, не є потенційним запахом коду.

Тепер, якщо вам потрібно чомусь відстежувати завдання, то описана вами техніка цілком розумна.


6

Так, як правило, асинхронізація недійсних обробників подій є єдиним випадком. Якщо ви хочете дізнатися більше про це, ви можете подивитися чудове відео тут на каналі 9

The only case where this kind of fire-and-forget is appropriate is in top-level event-handlers. Every other async method in your code should return "async Task".

ось посилання


«Оператори подій верхнього рівня » є важливим підказом. Під час використання асинхронного обробника недійсних подій на обробці подій нижчого рівня це може призвести до величезних проблем із не зловленими винятками.
— Портикус

Дякую за відео посилання, дуже корисне
— lsp

5

Якщо ви використовуєте ReSharper, вам може бути корисно безкоштовне рекомендоване розширення . Він аналізує методи "асинхронізовані порожнечі" та виділяє при неправильному використанні. Розширення може розрізняти різні способи використання асинхроністих пустот і надавати відповідні швидкі виправлення, описані тут: Вікі для перегляду рекомендацій .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.