Czy użycie sufiksu „Async” w nazwie metody zależy od tego, czy używany jest modyfikator „async”?

107

Jaka jest konwencja dodawania sufiksów do nazw metod z „Async”?

Czy przyrostek „Async” powinien być dodawany tylko do metody zadeklarowanej za pomocą asyncmodyfikatora?

public async Task<bool> ConnectAsync()

Czy wystarczy, że metoda po prostu zwróci Task<T>lub Task?

public Task<bool> ConnectAsync()
kasperhj
źródło
4
Jeśli chodzi o nazewnictwo, dokument TAP mówi: Metody asynchroniczne w TAP zawierają sufiks Async po nazwie operacji; na przykład GetAsync dla operacji pobierania. Jeśli dodajesz metodę TAP do klasy, która już zawiera tę nazwę metody z sufiksem Async, zamiast tego użyj sufiksu TaskAsync. Na przykład jeśli klasa ma już metodę GetAsync, użyj nazwy GetTaskAsync.
James Manning
4
ok, chyba pomylił mnie tytuł pytania „Konwencja nazewnictwa dla metod asynchronicznych”
James Manning
1
To jest słabo skonstruowane pytanie. Ludzie się kłócą, niejednoznaczne odpowiedzi.
Luke Puplett
4
Ponieważ wielu ludzi źle to zrozumiało i spiera się o to, co zostało zadane, zastanawiając się, czy jest to pytanie dwuczęściowe itp. Dowodem na to, że jest to zagmatwane, jest to, że ludzie są zdezorientowani.
Luke Puplett
2
@DavidRR Do dziś nadal nie rozumiem, ile zamieszania spowodowało to pytanie. Jeśli Twoje zmiany wprowadzają pewien porządek w zamieszaniu, tak że pomogło to tobie i prawdopodobnie może pomóc innym, to cieszę się z twoich zmian, ponieważ osiągnąłeś coś, czego nie mogłem w oryginalnym sformułowaniu. Pytanie jest teraz tak stare, że ledwo przypominam sobie mój sposób myślenia, kiedy zadałem je tutaj, więc pierwotny zamiar jest mniej ważny. Odpowiedź Łukasza świadczy o tym, że nie wszyscy byli zdezorientowani. Uważam, że jest to niezwykle pomocne.
kasperhj

Odpowiedzi:

128

Myślę, że prawda jest niejednoznaczna nawet z dokumentacji Microsoftu:

W programie Visual Studio 2012 i .NET Framework 4.5 każda metoda, której przypisano asyncsłowo kluczowe ( Asyncw języku Visual Basic), jest uważana za metodę asynchroniczną, a kompilatory C # i Visual Basic wykonują niezbędne transformacje, aby zaimplementować metodę asynchronicznie przy użyciu TAP. Metoda asynchroniczna powinna zwracać obiekt Tasklub Task<TResult>obiekt.

http://msdn.microsoft.com/en-us/library/hh873177(v=vs.110).aspx

To już nie w porządku. Każda metoda z asyncjest asynchroniczna, a następnie mówi, że powinna zwracać albo Tasklub Task<T>- co nie jest właściwe dla metod na górze stosu wywołań, na przykład Button_Click lub async void.

Oczywiście trzeba się zastanowić, jaki jest cel tej konwencji?

Można powiedzieć, że Asynckonwencja sufiksu polega na komunikowaniu użytkownikowi API, że metoda jest oczekiwana. Aby metoda była oczekiwana, musi zwrócić Taskdla void lub Task<T>dla metody zwracającej wartość, co oznacza, że ​​sufiksem może być tylko ta ostatnia Async.

Lub możesz powiedzieć, że Async konwencja sufiksu polega na komunikowaniu, że metoda może powrócić natychmiast, porzucając bieżący wątek w celu wykonania innej pracy i potencjalnie powodując wyścigi.

Ten cytat z dokumentu Microsoft mówi:

Zgodnie z konwencją dodajesz „Async” do nazw metod, które mają modyfikator Async lub async.

http://msdn.microsoft.com/en-us/library/hh191443.aspx#BKMK_NamingConvention

Co nawet nie wspomina, że ​​zwracanie własnych metod asynchronicznych Taskwymaga Asyncsufiksu, co, jak sądzę, wszyscy się zgadzamy.


Zatem odpowiedź na to pytanie może brzmieć: jedno i drugie. W obu przypadkach musisz dołączyć Asyncdo metod asyncsłowo kluczowe i zwrot Tasklub Task<T>.


Mam zamiar poprosić Stephena Tubusa o wyjaśnienie sytuacji.

Aktualizacja

Więc zrobiłem. A oto, co napisał nasz dobry człowiek:

Jeśli metoda publiczna zwraca Task i ma charakter asynchroniczny (w przeciwieństwie do metody, o której wiadomo, że zawsze jest wykonywana synchronicznie do zakończenia, ale z jakiegoś powodu nadal zwraca Task), powinna mieć przyrostek „Async”. To jest wskazówka. Podstawowym celem nazewnictwa jest pokazanie konsumentowi funkcjonalności, że wywoływana metoda prawdopodobnie nie zakończy synchronicznie całej swojej pracy; oczywiście pomaga to również w przypadku, gdy funkcjonalność jest ujawniana zarówno przy użyciu metod synchronicznych, jak i asynchronicznych, tak że do ich rozróżnienia potrzebna jest różnica nazw. Sposób, w jaki metoda osiąga swoją asynchroniczną implementację, jest nieistotny dla nazewnictwa: czy async / await jest używane do uzyskania pomocy kompilatora, czy też typy i metody z System.Threading.Tasks są używane bezpośrednio (np. sol. TaskCompletionSource) nie ma tak naprawdę znaczenia, ponieważ nie ma to wpływu na podpis metody, jeśli chodzi o konsumenta metody.

Oczywiście zawsze są wyjątki od wytycznych. Najbardziej zauważalnym w przypadku nazewnictwa byłyby przypadki, w których racją bytu całego typu jest zapewnienie funkcjonalności zorientowanej na asynchronię, w którym to przypadku posiadanie Async na każdej metodzie byłoby przesadą, np. Metody na samym Task, które generują inne zadania .

Jeśli chodzi o metody asynchroniczne zwracające void, nie jest pożądane, aby znajdowały się w obszarze publicznym, ponieważ obiekt wywołujący nie ma dobrego sposobu na sprawdzenie, kiedy praca asynchroniczna została zakończona. Jeśli jednak musisz publicznie ujawnić metodę asynchroniczną zwracającą void, prawdopodobnie chcesz mieć nazwę, która będzie wskazywać, że praca asynchroniczna jest inicjowana, i możesz użyć tutaj sufiksu „Async”, jeśli ma to sens. Biorąc pod uwagę, jak rzadki powinien być ten przypadek, twierdzę, że to tak naprawdę decyzja indywidualna.

Mam nadzieję, że to pomoże, Steve

Zwięzłe wskazówki zawarte w pierwszym zdaniu Stefana są wystarczająco jasne. Wyklucza, async voidponieważ tworzenie publicznego API o takim projekcie jest niezwykłe, ponieważ prawidłowym sposobem implementacji asynchronicznej pustki jest zwrócenie prostej Taskinstancji i pozwolenie kompilatorowi na jego magię. Jeśli jednak chcesz mieć public async voidrozszerzenie , zalecamy dołączenie Async. Inne async voidmetody top-of-stack , takie jak programy obsługi zdarzeń, zwykle nie są publiczne i nie mają znaczenia / kwalifikują się.

Dla mnie mówi mi, że jeśli zastanawiam się nad dodawaniem sufiksu Asyncdo a async void, prawdopodobnie powinienem zmienić go na znak , async Taskaby dzwoniący mogli na niego poczekać, a następnie dołączyć Async.

Luke Puplett
źródło
20
Cóż, szkoda, że ​​nie mamy sprawdzania czasu kompilacji dla wywołań metod .. och, czekaj. Gdybym nazwać metoda Get lub GetAsync i nie używać Oczekujcie od wywołującego strony, kompilacji nie powiedzie się zbudować. Ta konwencja jest więc SILLY i naprawdę jest sprzeczna z wieloma wytycznymi Microsoft Style, takimi jak unikanie rzeczy takich jak PersonStringlub PriceDecimaltak po co używać GetAsync- konsumenci API asynchronicznego API nie muszą się tym martwić, ponieważ żądanie zawsze wraca po zakończeniu wszystkich zadań. To głupie i naprawdę denerwujące. Ale to tylko kolejna konwencja, że ​​nikt tak naprawdę nie wie, dlaczego tam jest.
Piotr Kula
2
@ppumkin: Jak zauważył Stephen, metoda może łatwo być asynchroniczna z natury bez użycia async / await, więc obiekt wywołujący nie ma żadnego innego wskazania niż nazwa, czy funkcja działa asynchronicznie.
Hannobo,
6
@ppumkin: domyślnie nie oczekiwanie na metodę asynchroniczną powoduje wyświetlenie ostrzeżenia w czasie kompilacji; nie jest to błąd kompilacji.
Dustin Cleveland
7
Uważam tę konwencję za głupią. Istnieją trzy automatyczne wskazania, że ​​metoda jest asynchroniczna: 1. Typ zwracany to Task. 2. Uzupełnianie kodu stanowi nieoczekiwaną wskazówkę. 3. IDE ostrzeże Cię, podkreślając na zielono i wyświetlając ostrzeżenie kompilatora. Więc całkowicie zgadzam się z @ppumkin. Sufiks Async jest tak głupi, jakbyś napisał właściwość taką jak ta: public Lazy <Customer> CustomerLazy. Kto by to zrobił! ??
Marco
2
@Marco Podniosłem ten pomysł na GitHubie, pomyślałem, że to najlepsze miejsce, ale nie mam z nim zaangażowania, pomyślałem, że dostanę: github.com/dotnet/core/issues/1464
Luke Puplett
68

Tworzę wiele usług API i innych aplikacji, które wywołują inne systemy, w których większość mojego kodu działa asynchronicznie.

Oto moja praktyczna zasada:

Jeśli istnieje metoda nie-asynchroniczna i asynchroniczna, które zwracają to samo, sufikuję tę asynchroniczną z Async. W przeciwnym razie nie.

Przykłady:

Tylko jedna metoda:

public async Task<User> GetUser() { [...] }

Ta sama metoda z dwoma podpisami:

public User GetUser() { [...] }

public async Task<User> GetUserAsync() { [...] }

Ma to sens, ponieważ zwracane są te same dane, ale jedyną różnicą jest sposób ich zwracania , a nie same dane.

Myślę też, że te konwencje nazewnictwa istnieją ze względu na potrzebę wprowadzenia metod asynchronicznych i nadal zachowują kompatybilność wsteczną.

Twierdzę, że nowy kod nie powinien używać sufiksu Async. Jest to tak samo oczywiste, jak zwracany typ String lub Int, jak wspomniano wcześniej w tym wątku.

Jonas Stensved
źródło
8
Zgadzam się, tym bardziej, że zazwyczaj trzeba iść 'async all the way', w którym to przypadku przyrostek jest zbędny - po co dopisywać go do 90% kodu;)
Bartosz
3
to jest najlepsze rozwiązanie. Nie zauważając tego, robiłem to samo w moich interfejsach API.
Marco
2
To jest lepsze niż przyrostka wnioskach „Async” wszystkich metod aplikacji asynchronicznym
Mariusz Jamro
Problem z tą techniką polega na tym, że jeśli później utworzysz wersję inną niż asynchroniczna, nie możesz użyć swojej preferowanej nazwy „GetUser ()”.
David
3
To jest pragmatyczna droga. Dołączanie Async do każdej metody, która ma modyfikator async, to po prostu węgierska notacja 2019. @David, jeśli w końcu dodasz wersję inną niż asynchroniczna później, zmień nazwy metod i postępuj zgodnie z konwencją nazewnictwa lub po prostu tego nie rób.
Skrymsli
25

Jaka jest konwencja dodawania sufiksów do nazw metod z „Async”.

Te zadania opartego Asynchroniczna Wzór (TAP) mówi, że metody powinny zawsze zwraca Task<T>(a Task) i są nazwane z Async końcówką; jest to niezależne od użycia async. Oba Task<bool> Connect()i będą się kompilować i działać dobrze, ale nie będziesz przestrzegać konwencji nazewnictwa TAP.asyncTask<bool> Connect()

Czy metoda powinna zawierać asyncmodyfikator, czy wystarczy, że po prostu zwraca Task?

Jeśli treść metody (niezależnie od zwracanego typu lub nazwy) zawiera await, należy użyć async; a kompilator powie Ci „Operator 'await' może być używany tylko w ramach metody asynchronicznej. ...”. Zwrot Task<T>lub Tasknie jest wystarczający, aby uniknąć używania async. Aby uzyskać szczegółowe informacje, zobacz async (odwołanie w C #) .

Czyli które z tych podpisów są poprawne:

Obie i prawidłowo przestrzegaj konwencji TAP. Zawsze możesz użyć słowa kluczowego, ale otrzymasz ostrzeżenie kompilatora „Ta metoda asynchroniczna nie ma operatorów„ await ”i będzie działać synchronicznie. ...”, jeśli ciało nie używa .asyncTask<bool> ConnectAsync()Task<bool> ConnectAsync()asyncawait

Ðаn
źródło
1
Odnosi się do tego, czy dodajesz „Async” do nazwy metody, a nie czy używasz asyncsłowa kluczowego.
Servy
1
@Servy, czy nie używać słowa kluczowego, asyncto druga część pytania.
Corak
2
@Servy Pytanie składa się z dwóch części. Pierwsza część dotyczy tego, czy dodać „Async” do nazwy metody. Druga część dotyczy tego, czy należy użyć asyncmodyfikatora. Zobacz także przykłady OP, public async Task<bool> ConnectAsync()(z asyncmodyfikatorem) vs public Task<bool> ConnectAsync()(bez asyncmodyfikatora). Sama nazwa metody ma w obu przypadkach przyrostek „Async”.
Corak
2
To nie jest dwuczęściowe pytanie. Pytanie brzmi, czy „Async” powinno być dołączone do nazw metod zwracanych Tasklub metod, które mają async Task.
kasperhj
3
@lejon: powinieneś poprawić pytanie; „vs.” fragmenty kodu wyjaśniają, że pytanie (w całości) dotyczy asynchronizacji, ponieważ to jedyna różnica.
аn
11

czy wystarczy, że po prostu zwraca Task?

Że. Słowo asynckluczowe nie jest tutaj prawdziwym problemem. Jeśli zaimplementujesz asynchroniczność bez użycia asyncsłowa kluczowego, metoda jest nadal „Async” w sensie ogólnym.

Servy
źródło
7

Ponieważ Taski Task<T>to zarówno awaitable typy stanowią one część działanie asynchroniczne. A przynajmniej powinni reprezentować.

Należy dodać sufiks Asyncdo metody, która w niektórych przypadkach (niekoniecznie we wszystkich) nie zwraca wartości, a raczej zwraca opakowanie wokół trwającej operacji. Zwykle jest to opakowanie Task, ale w systemie Windows RT może to być IAsyncInfo. Podążaj za swoim przeczuciem i pamiętaj, że jeśli użytkownik twojego kodu zobaczy Asyncfunkcję, będzie wiedział, że wywołanie tej metody jest oddzielone od wyniku tej metody i że musi odpowiednio działać.

Zauważ, że istnieją metody, takie jak Task.Delayi, Task.WhenAllktóre zwracają, Taskale nie mają rozszerzeniaAsync sufiksu.

Zauważ również, że istnieją async voidmetody, które reprezentują metodę asynchroniczną odpal i zapomnij i powinieneś mieć świadomość, że metoda jest zbudowana w taki sposób.

Toni Petrina
źródło
6

Twierdziłbym, że powinien używać sufiksu Async, jeśli zwraca Task, niezależnie od tego, czy metoda jest zadeklarowana z asyncmodyfikatorem, czy nie.

Powodem jest to, że nazwa jest zadeklarowana w interfejsie. Interfejs deklaruje zwracany typ, którym jest Task. Następnie istnieją dwie implementacje tego interfejsu, jedna implementacja implementuje go za pomocą asyncmodyfikatora, a druga nie.

public interface IFoo
{
    Task FooAsync();
}

public class FooA : IFoo
{
    public Task FooAsync() { /* ... */ }
}

public class FooB : IFoo
{
    public async Task FooAsync() { /* ... */ }
}
Fred
źródło
To jest takie prawdziwe. Zawsze używamy interfejsów, wszędzie, a interfejsów nie można zadeklarować jako asynchronicznych. Więc oficjalne przewodniki po używaniu sufiksu Async wydają mi się kompletnie bezsensowne. Myślę, że słowo kluczowe async jest tylko szczegółem implementacyjnym, częścią elementów wewnętrznych metody i nie powinno wpływać na jej nazwę ani cokolwiek zewnętrznego.
Al Kepp
5

W programowaniu asynchronicznym z async i await (C #) firma Microsoft oferuje następujące wskazówki:

Konwencja nazewnictwa

Zgodnie z konwencją dodajesz „Async” do nazw metod, które mają asynchroniczne modyfikator .

Możesz zignorować konwencję, w której zdarzenie, klasa bazowa lub kontrakt interfejsu sugeruje inną nazwę. Na przykład nie należy zmieniać nazw typowych programów obsługi zdarzeń, takich jak Button1_Click.

Uważam, że te wskazówki są niekompletne i niezadowalające. Czy to oznacza, że ​​w przypadku braku asyncmodyfikatora ta metoda powinna być nazwana Connectzamiast ConnectAsync?

public Task<bool> ConnectAsync()
{
    return ConnectAsyncInternal();
}

Nie sądzę. Jak wskazano w odpowiedzi zwięzłej przez @Servy i bardziej szczegółową odpowiedź przez @Luke Puplett , wierzę, że jest to właściwe i rzeczywiście oczekiwać, że metoda ta powinna być nazwane ConnectAsync(ponieważ zwraca awaitable). Na dalsze poparcie, @John Skeet w tej odpowiedzi na inne pytanie dołącza Asyncdo nazwy metody, niezależnie od obecności asyncmodyfikatora.

Wreszcie, w innym pytaniu , należy rozważyć ten komentarz przez @Damien_The_Unbeliever :

async/awaitto szczegóły implementacji twoich metod. Nie ma znaczenia, czy twoja metoda jest zadeklarowana, async Task Method()czy tylko Task Method(), jeśli chodzi o twoich wywołujących . (W rzeczywistości możesz swobodnie przełączać się między tymi dwoma w późniejszym czasie, bez uznawania tego za przełomową zmianę).

Z tego wnioskuję, że to asynchroniczny charakter metody decyduje o tym, jak należy ją nazwać. Użytkownik metody nie będzie nawet wiedział, czy asyncmodyfikator jest używany w jej implementacji (bez kodu źródłowego C # lub CIL).

DavidRR
źródło