Devo evitare i gestori di eventi "async void"?


119

So che è generalmente considerata una cattiva idea usare il fuoco e dimentica async void metodi per avviare le attività, perché non c'è traccia dell'attività in sospeso ed è difficile gestire le eccezioni che potrebbero essere lanciate all'interno di un tale metodo.

In genere dovrei evitare anche i async voidgestori di eventi? Per esempio,

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

Posso riscriverlo in questo modo:

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
        // ...
} 

Quali sono le rocce sottomarine per i gestori di eventi asincroni, oltre al possibile rientro?


Dovresti ma non puoi. Oltre a ciò, tutte le cure che devi prestare quando usi il void asincrono sono già richieste dai gestori di eventi dell'interfaccia utente.
— Paulo Morgado

E il rientro avviene a causa di operazioni asincrone attivate dal gestore di eventi e non dall'uso di async-await da solo.
— Paulo Morgado

Risposte:


153

La linea guida è da evitare async void tranne quando viene utilizzato in un gestore di eventi, quindi l'utilizzo async voidin un gestore di eventi è OK.

Detto questo, per ragioni di unit test spesso mi piace escludere la logica di tutti i async voidmetodi. Per esempio,

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);
}

Sono curioso ... c'è un motivo per cui non modifichi semplicemente Form_Loadl'accesso a public? Sembra che il codice sarebbe meno prolisso in questo modo.
— InteXX

Oops, non importa ... VBer sta cercando di leggere C # qui ... Ho appena notato il tipo restituito di OnFormLoadAsync. Ora vedo che questo è un trucco utile. Grazie.
— InteXX

Detto questo, potresti dare un'occhiata e offrire un'opinione qui . Grazie!
— InteXX

2
@ AlexHopeO'Connor: il Handledflag deve essere impostato in modo sincrono; non è possibile utilizzare asyncper prendere una decisione sulla gestione o meno dell'evento.
— Stephen Cleary

2
@ AlexHopeO'Connor: È passato un po 'di tempo dall'ultima volta che ho lavorato con un'app WPF, ma ho utilizzato soluzioni simili in passato. Cioè, crea il ICommand.Executemetodo async void; Lo considero accettabile poiché ICommand.Executeè logicamente un gestore di eventi.
— Stephen Cleary

50

In genere dovrei evitare anche i gestori di eventi void asincroni?

Generalmente i gestori di eventi sono l'unico caso in cui un metodo asincrono vuoto non è un potenziale odore di codice.

Ora, se hai bisogno di monitorare l'attività per qualche motivo, la tecnica che descrivi è perfettamente ragionevole.


6

Sì, in genere il vuoto asincrono dei gestori di eventi è l'unico caso. Se vuoi saperne di più, puoi guardare un ottimo video qui sul canale 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".

ecco il link


I " gestori di eventi di primo livello " sono un suggerimento importante. Quando si utilizza un gestore di eventi void asincrono su un gestore di eventi di livello inferiore, può causare enormi problemi con eccezioni non rilevate.
— Portikus

Grazie per il collegamento al video, molto utile
— lsp

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.