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.