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.