2015-09-24 13 views
7

Program Visual Studio wysyła ostrzeżenie dotyczące tego kodu ("ponieważ wywołanie to nie jest oczekiwane, wykonanie bieżącej metody jest kontynuowane przed zakończeniem połączenia").Czy można zadeklarować metodę asynchroniczną jako zwracającą pustkę, aby uciszyć ostrzeżenie CS4014?

static void Main(string[] args) 
{ 
    FireAndForget(); // <-- Warning CS4014 
    // Do something else. 
} 

static async Task FireAndForget() 
{ 
    // Do something (cannot throw). 
} 

moim rozumieniu jest to, że jest OK, aby nie czekać do tego zadania w tym konkretnym przypadku, ponieważ fire-and-forget nigdy rzuci wyjątek.

Zamiast wyłączać ostrzeżenie z pragmą, rozważałem zmianę typu powrotu FireAndForget z zadania na nieważne. To skutecznie wycisza kompilator.

static async void FireAndForget() // <-- Task changed to void 
{ 
    // Do something (cannot throw). 
} 

Jednak według Stephen Cleary „asynchroniczny” void metod należy unikać, więc nie jestem pewien, co zrobić.

Czy można mieć metodę "async void", jeśli metoda nie została zaprojektowana jako pierwsza i jeśli nie zostanie zgłoszony żaden wyjątek?

+0

* Metoda nie została zaprojektowana jako pierwsza na co dzień. * Co to znaczy? Co właściwie robi 'FireAndForget'? –

+3

Dlaczego do cholery zostało to odrzucone + głosowałem za zamknięciem? – ken2k

+0

@ ken2k Nie głosowałem, aby zamknąć, ale widzę, gdzie byłoby to postrzegane jako pytanie oparte na opiniach bez obiektywnie poprawnej odpowiedzi. Być może "Czy to jest OK" można przeformułować na "Jakie są zalety i wady". – pseudocoder

Odpowiedz

9

Niezwykle trudno jest uzyskać prawdziwą operację przeciwpożarową; to jest operacja, w której:

  • Nikt nie dba o to, kiedy się zakończy.
  • Nikt się nie przejmuje, jeśli się uda.
  • Nikt się nie przejmuje, jeśli zgłasza wyjątek.

Zwłaszcza z ostatnią z nich; większość tak zwanych operacji "pożegnaj i zapomnij" nie jest w rzeczywistości pożarem i zapomnieniem, ponieważ trzeba podjąć pewne działania, jeśli się to nie uda.

To powiedziawszy, jest kilka sytuacji, w których można zastosować prawdziwy ogień i zapomnieć.

wolę używać async Task i uniknąć ostrzeżenia kompilatora przypisując zadanie inaczej nieużywanego zmiennej:

var _ = FireAndForget(); 

async Task metody są bardziej wielokrotnego użytku i sprawdzalne niż async void metod.

Jednak nie zgodziłbym się, gdyby programista z mojego zespołu użył zamiast tego async void.

+0

Trudno mi uwierzyć, że powinno to być rzadkie. Bez metod "zapominania o ogniu", czy "asynchronizacja" nie stałaby się wirusowa i nie rozprzestrzeniłaby się na Twoją główną metodę (gdzie chciałbyś "poczekać")? – ZunTzu

+0

Stephen, mam kolejne pytanie. Próbowałem przypisać zadanie do zmiennej, jak w twoim przykładzie. Rzeczywiście wycisza kompilator, ale zadanie nie jest jeszcze oczekiwane. Czy w takim przypadku kompilator nie powinien również wysyłać ostrzeżenia? – ZunTzu

+1

@ZunTzu: 1) Tak; tak działa większość programów asynchronicznych (> 98%). 2) Kompilator stara się ostrzec, ale nie jest (nie może być) doskonały. –

2

Czy można mieć „asynchronicznej void” metodę jeżeli metoda nie jest przeznaczony być awaitable w pierwszej kolejności, a jeśli nie zostanie wyrzucony wyjątek?

Chociaż może być "OK", aby to zrobić, nadal zachęcam do wykonania metody async Task. Nawet jeśli jesteś w 100% pewny, że ta metoda nie zostanie rzucona i nie jest oczekiwana, nigdy nie wiesz, jak to się skończy. Teraz już w pełni zdajesz sobie sprawę, jakie są konsekwencje używania async void, ale jeśli istnieje niewielka szansa, że ​​ktoś może potrzebować tego użyć w przyszłości, lepiej jest umieścić komentarz na temat tego, dlaczego ten Task nie jest oczekiwano, zamiast iść z łatwą ścieżką tworzenia tego void.

Nie pozwól, aby ostrzeżenia kompilatora martwiły Cię, powiedziałbym, że martwisz się poprawnością i skutkami, jakie może to mieć w Twoim kodzie.

+0

Zmiana "pustki" na "Zadanie" tylko dlatego, że metoda jest "asynchroniczna", jest niewłaściwa. Oto dlaczego. Jedynym celem "asynchronizacji" jest odblokowanie możliwości wykorzystania "oczekujących" w treści metody. Moim zdaniem oznacza to, że "asynchronizacja" nie jest częścią zewnętrznej umowy dotyczącej metody. Zmiana "void" na "Task" na contratry zmienia tę zewnętrzną umowę. – ZunTzu

+0

@ZunTzu Ale ujawnia prawdziwą operację metody. Kiedy widzę metodę zwracającą 'void', zakładam, że jest ona synchroniczna. Wywoływanie takiej metody, która kończy się, ale jest operacją leżącą u jej podstaw, nie jest bardziej mylące i może kłamać dzwoniącemu. –

+0

z reguły dzwoniący nie może stwierdzić, czy uruchomię wątek roboczy, czy zadzwonię do usługi internetowej. Dlaczego dzwoniący powinien wiedzieć, czy korzystam z "czekania"? Wydaje mi się, że jest to naruszenie zasady enkapsulacji. – ZunTzu

0

Jednym z potencjalnych problemów jest to, że nie będzie można stwierdzić, czy kod wyłączył wyjątek, czy nie. Więc jeśli masz testy jednostkowe w celu ich wykrycia, testy jednostkowe nigdy nie będą działać.

Klasyczny przykład ze strony MSDN:

 
private async void ThrowExceptionAsync() 
{ 
    throw new InvalidOperationException(); 
} 
public void AsyncVoidExceptions_CannotBeCaughtByCatch() 
{ 
    try 
    { 
    ThrowExceptionAsync(); 
    } 
    catch (Exception) 
    { 
    // The exception is never caught here! 
    throw; 
    } 
} 

http://haacked.com/archive/2014/11/11/async-void-methods/

https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

Zamiast async void, czy jest to przypadek szczególny krawędź, jak o użyciu instrukcji PRAGMA?

#pragma warning disable CS-4014 
... your code here ... 
#pragma warning restore CS-4014 

W ten sposób można wyłączyć zakłócenia statyczne.

HTH ...

+0

Jestem świadomy tego ograniczenia. Właśnie dlatego wyraźnie napisałem, że FireAndForget nie może rzucić wyjątku. – ZunTzu

+0

Tak, specyficzne zamierzone użycie 'async void' jest tak naprawdę dla procedur obsługi zdarzeń, więc powinno się unikać jakiegokolwiek zewnętrznego użycia, IMHO. Zamiast tego użyj pragmy. – code4life

Powiązane problemy