Czy powinienem unikać programów obsługi zdarzeń „async void”?


119

Wiem, że generalnie uważane jest za zły pomysł, aby async voiduruchamiać zadania przy użyciu metod odpal i zapomnij , ponieważ nie ma ścieżki oczekującego zadania i trudno jest obsłużyć wyjątki, które mogą zostać wrzucone do takiej metody.

Czy powinienem również unikać async voidobsługi zdarzeń? Na przykład,

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

Mogę to przepisać w ten sposób:

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

Jakie są podwodne skały dla obsługi zdarzeń asynchronicznych, oprócz możliwego ponownego wejścia?


Powinieneś, ale nie możesz. Poza tym wszystkie uwagi, które musisz podjąć podczas korzystania z asynchronicznego void, są już wymagane przez programy obsługi zdarzeń interfejsu użytkownika.
— Paulo Morgado

Ponowne wejście dzieje się z powodu operacji asynchronicznych uruchamianych przez procedurę obsługi zdarzeń, a nie przez samo użycie async-await.
— Paulo Morgado,

Odpowiedzi:


153

Wytyczną jest unikanie async void z wyjątkiem sytuacji, gdy jest używana w programie obsługi zdarzeń, więc używanie async voidw programie obsługi zdarzeń jest OK.

To powiedziawszy, z powodów związanych z testami jednostkowymi często lubię brać pod uwagę logikę wszystkich async voidmetod. Na przykład,

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

Jestem ciekawy ... czy jest powód, dla którego nie zmieniasz po prostu Form_Loaddostępu do public? Wygląda na to, że w ten sposób kod byłby mniej szczegółowy.
— InteXX,

Ups, nieważne ... VBer próbuje czytać C # tutaj ... Właśnie zauważyłem zwracany typ OnFormLoadAsync. Teraz widzę, że to przydatna sztuczka. Dzięki.
— InteXX,

To powiedziawszy, czy mógłbyś rzucić okiem i wyrazić tutaj opinię . Dzięki!
— InteXX,

2
@ AlexHopeO'Connor: Handledflaga musi być ustawiona synchronicznie; nie można użyć go asyncdo podjęcia decyzji, czy zdarzenie jest obsługiwane, czy nie.
— Stephen Cleary

2
@ AlexHopeO'Connor: Minęło trochę czasu, odkąd pracowałem z aplikacją WPF, ale korzystałem z rozwiązań podobnych do tego w przeszłości. To znaczy utwórz ICommand.Executemetodę async void; Uważam to za dopuszczalne, ponieważ logicznieICommand.Execute jest to program obsługi zdarzeń.
— Stephen Cleary

50

Czy powinienem również unikać asynchronicznych programów obsługi zdarzeń void?

Ogólnie rzecz biorąc, programy obsługi zdarzeń to jedyny przypadek, w którym metoda void async nie jest potencjalnym zapachem kodu.

Jeśli z jakiegoś powodu musisz śledzić zadanie, to technika, którą opisujesz, jest całkowicie rozsądna.


6

Tak, na ogół asynchroniczny brak obsługi zdarzeń jest jedynym przypadkiem. Jeśli chcesz dowiedzieć się więcej na ten temat, obejrzyj świetny film na kanale 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".

tutaj jest link


W „ Event-koparki do najwyższego poziomu ” jest ważna wskazówka. Podczas korzystania z programu obsługi zdarzeń asynchronicznych void na module obsługi zdarzeń niższego poziomu może to powodować ogromne problemy z nie przechwyconymi wyjątkami.
— Portikus

Dzięki za link do wideo, bardzo przydatne
— lsp

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.