Hängt die Verwendung des Suffixes "Async" in einem Methodennamen davon ab, ob der Modifikator "async" verwendet wird?

106

Was ist die Konvention zum Suffixieren von Methodennamen mit "Async"?

Sollte das Suffix "Async" nur an eine Methode angehängt werden, die mit dem asyncModifikator deklariert wurde ?

public async Task<bool> ConnectAsync()

Oder reicht es aus, dass die Methode nur zurückgibt Task<T>oder Task?

public Task<bool> ConnectAsync()
kasperhj
quelle
4
Für den Benennungsteil heißt es im TAP-Dokument : Asynchrone Methoden in TAP enthalten das Async-Suffix nach dem Operationsnamen. Beispiel: GetAsync für eine Get-Operation. Wenn Sie einer Klasse, die diesen Methodennamen bereits mit dem Suffix Async enthält, eine TAP-Methode hinzufügen, verwenden Sie stattdessen das Suffix TaskAsync. Wenn die Klasse beispielsweise bereits über eine GetAsync-Methode verfügt, verwenden Sie den Namen GetTaskAsync.
James Manning
4
ok, ich glaube, ich war verwirrt über den Fragentitel "Namenskonvention für asynchrone Methoden"
James Manning
1
Dies ist eine schlecht konstruierte Frage. Menschen streiten, zweideutige Antworten.
Luke Puplett
4
Weil viele Leute es missverstanden haben und darüber streiten, was tatsächlich gefragt wird, und sich fragen, ob es sich um eine zweiteilige Frage handelt usw. Der Beweis dafür, dass es verwirrend ist, ist, dass die Leute verwirrt sind.
Luke Puplett
2
@DavidRR Bis heute verstehe ich die Verwirrung, die diese Frage anscheinend verursacht hat, immer noch nicht. Wenn Ihre Änderungen eine gewisse Ordnung in die Verwirrung bringen, so dass es Ihnen geholfen hat und möglicherweise anderen helfen kann, dann begrüße ich Ihre Änderungen, denn Sie haben etwas erreicht, das ich in der ursprünglichen Formulierung nicht erreichen konnte. Die Frage ist jetzt so alt, dass ich mich kaum an meine Denkweise erinnern kann, als ich sie hier gestellt habe, und daher ist die ursprüngliche Absicht weniger wichtig. Lukes Antwort spiegelt wider, dass nicht alle verwirrt waren. Ich fand es sehr hilfreich.
Kasperhj

Antworten:

126

Ich denke, die Wahrheit ist selbst aus der Microsoft-Dokumentation nicht eindeutig:

In Visual Studio 2012 und .NET Framework 4.5 wird jede Methode, die dem asyncSchlüsselwort ( Asyncin Visual Basic) zugeordnet ist, als asynchrone Methode betrachtet, und die C # - und Visual Basic-Compiler führen die erforderlichen Transformationen durch, um die Methode mithilfe von TAP asynchron zu implementieren. Eine asynchrone Methode sollte entweder ein Taskoder ein Task<TResult>Objekt zurückgeben.

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

Das stimmt schon nicht. Jede Methode mit asyncist asynchron und sagt dann, dass sie entweder ein Taskoder zurückgeben sollte Task<T>- was für Methoden am oberen Rand eines Aufrufstapels nicht richtig ist, z. B. Button_Click oder async void.

Natürlich müssen Sie überlegen, worum es bei der Konvention geht.

Man könnte sagen, dass die AsyncSuffix-Konvention darin besteht, dem API-Benutzer mitzuteilen, dass die Methode erwartet wird. Damit eine Methode erwartet werden kann, muss sie Taskfür eine Leere oder Task<T>für eine Methode zurückgegeben werden, die einen Wert zurückgibt. Dies bedeutet, dass nur die letztere mit einem Suffix versehen werden kann Async.

Oder Sie könnten sagen, dass die AsyncSuffix-Konvention darin besteht, zu kommunizieren, dass die Methode sofort zurückkehren kann, den aktuellen Thread freizugeben, um andere Arbeiten auszuführen, und möglicherweise Rennen zu verursachen.

In diesem Microsoft-Dokumentzitat heißt es:

Konventionell hängen Sie "Async" an die Namen von Methoden an, die einen Async- oder Async-Modifikator haben.

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

Was nicht einmal erwähnt, dass Ihre eigenen asynchronen Methoden, die zurückkehren, Taskdas AsyncSuffix benötigen , dem wir uns alle einig sind.


Die Antwort auf diese Frage könnte also lauten: beides. In beiden Fällen müssen Sie AsyncMethoden mit dem asyncSchlüsselwort anhängen , die Taskoder zurückgeben Task<T>.


Ich werde Stephen Toub bitten, die Situation zu klären.

Aktualisieren

So tat ich. Und hier ist, was unser guter Mann schrieb:

Wenn eine öffentliche Methode Task zurückgibt und asynchroner Natur ist (im Gegensatz zu einer Methode, die bekanntermaßen immer synchron zum Abschluss ausgeführt wird, aber aus irgendeinem Grund immer noch eine Task zurückgibt), sollte sie das Suffix "Async" haben. Das ist die Richtlinie. Das Hauptziel bei der Benennung besteht darin, dem Verbraucher die Funktionalität klar zu machen, dass die aufgerufene Methode wahrscheinlich nicht alle Arbeiten synchron abschließt. Dies hilft natürlich auch in Fällen, in denen die Funktionalität sowohl mit synchronen als auch mit asynchronen Methoden verfügbar gemacht wird, sodass Sie einen Namensunterschied benötigen, um sie zu unterscheiden. Wie die Methode ihre asynchrone Implementierung erreicht, spielt für die Benennung keine Rolle: ob async / await verwendet wird, um die Hilfe des Compilers zu erhalten, oder ob Typen und Methoden von System.Threading.Tasks direkt verwendet werden (e. G. TaskCompletionSource) spielt keine Rolle, da dies die Signatur der Methode für einen Konsumenten der Methode nicht beeinflusst.

Natürlich gibt es immer Ausnahmen von einer Richtlinie. Am bemerkenswertesten bei der Benennung sind Fälle, in denen die Existenzberechtigung eines ganzen Typs darin besteht, asynchrone Funktionen bereitzustellen. In diesem Fall wäre es übertrieben, Async für jede Methode zu haben, z. B. die Methoden für Task selbst, die andere Tasks erzeugen .

Bei asynchronen Methoden mit ungültiger Rückgabe ist es nicht wünschenswert, diese im öffentlichen Bereich zu haben, da der Anrufer nicht genau wissen kann, wann die asynchrone Arbeit abgeschlossen ist. Wenn Sie jedoch eine asynchrone Methode mit ungültiger Rückgabe öffentlich verfügbar machen müssen, möchten Sie wahrscheinlich einen Namen haben, der anzeigt, dass asynchrone Arbeit initiiert wird, und Sie könnten hier das Suffix "Async" verwenden, wenn dies sinnvoll ist. Angesichts der Seltenheit dieses Falls würde ich behaupten, dass es sich wirklich um eine Einzelfallentscheidung handelt.

Ich hoffe das hilft, Steve

Die prägnante Anleitung aus Stephens Eröffnungssatz ist klar genug. Es wird ausgeschlossen, async voidweil es ungewöhnlich ist, eine öffentliche API mit einem solchen Design erstellen zu wollen, da die korrekte Implementierung einer asynchronen Leere darin besteht, eine einfache TaskInstanz zurückzugeben und den Compiler seiner Magie zu überlassen. Wenn Sie jedoch eine wollten public async void, wird das Anhängen Asyncempfohlen. Andere Top-of-Stack- async voidMethoden wie Event-Handler sind normalerweise nicht öffentlich und spielen keine Rolle.

Für mich bedeutet dies, dass ich, wenn ich mich über das Suffixieren Asynceines frage async void, es wahrscheinlich in ein Suffix umwandeln sollte, async Taskdamit Anrufer darauf warten und dann anhängen können Async.

Luke Puplett
quelle
20
Schade, dass wir keine Überprüfungen der Kompilierungszeit für Methodenaufrufe haben. Oh, warte. Wenn ich den Namen der Methode Get oder GetAsync und nicht verwenden await von der anrufenden Seite, wird die Kompilierung zu bauen scheitern. Diese Konvention ist also DUMM und verstößt wirklich gegen viele Microsoft Style-Richtlinien, z. B. das Vermeiden von Dingen wie PersonStringoder PriceDecimalwarum GetAsync- API-Konsumenten von asynchronen APIs müssen sich darüber keine Gedanken machen, da die Anforderung immer zurückgegeben wird, nachdem alle Aufgaben ohnehin abgeschlossen sind. Es ist albern und nervt mich wirklich. Aber es ist nur eine andere Konvention, von der niemand wirklich weiß, warum sie dort ist.
Piotr Kula
2
@ppumkin: Wie Stephen betonte, kann eine Methode ohne Verwendung von async / await leicht asynchron sein, sodass der Aufrufer nur den Namen angibt, ob die Funktionalität asynchron ausgeführt wird oder nicht.
Hannobo
6
@ppumkin: Wenn eine asynchrone Methode standardmäßig nicht abgewartet wird, wird eine Warnung zur Kompilierungszeit ausgegeben. kein Build-Fehler.
Dustin Cleveland
7
Ich finde diese Konvention albern. Es gibt drei automatische Anzeigen, dass eine Methode asynchron ist: 1. Der Rückgabetyp ist Task. 2. Die Code-Vervollständigung enthält einen erwarteten Hinweis. 3. Die IDE warnt Sie, indem sie grün unterstreicht und eine Compiler-Warnung anzeigt. Also stimme ich @ppumkin voll und ganz zu. Das Async-Suffix ist so albern, als hätten Sie eine Eigenschaft wie diese geschrieben: public Lazy <Kunde> CustomerLazy. Wer würde das tun! ??
Marco
2
@Marco Ich habe diese Idee auf GitHub angesprochen, dachte, es wäre der beste Ort, aber ich habe nicht die Verlobung damit, von der ich dachte, dass ich sie bekommen würde: github.com/dotnet/core/issues/1464
Luke Puplett
68

Ich erstelle viele API-Dienste und andere Anwendungen, die andere Systeme aufrufen, auf denen der größte Teil meines Codes asynchron ausgeführt wird.

Meine eigene Faustregel, der ich folge, lautet:

Wenn es sowohl eine nicht asynchrone als auch eine asynchrone Methode gibt, die dasselbe zurückgeben, füge ich die asynchrone mit Async hinzu. Sonst nicht.

Beispiele:

Nur eine Methode:

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

Gleiche Methode mit zwei Signaturen:

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

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

Dies ist sinnvoll, da dieselben Daten zurückgegeben werden, aber das einzige, was sich unterscheidet, ist die Art der Rückgabe von Daten , nicht die Daten selbst.

Ich denke auch, dass diese Namenskonventionen aufgrund der Notwendigkeit existieren, asynchrone Methoden einzuführen und dennoch die Abwärtskompatibilität aufrechtzuerhalten.

Ich behaupte, dass neuer Code nicht das Async-Suffix verwenden sollte. Es ist genauso offensichtlich wie der Rückgabetyp von String oder Int, wie zuvor in diesem Thread erwähnt.

Jonas Stensved
quelle
8
Ich bin damit einverstanden, insbesondere, dass Sie normalerweise "den ganzen Weg asynchron" gehen müssen. In diesem Fall ist das Suffix redundant - was bringt es, es an 90% des Codes
Bartosz
3
Dies ist die beste Lösung. Ohne es zu bemerken, habe ich in meinen APIs das Gleiche getan.
Marco
2
Dies ist viel besser als das Suffix mit "Async" aller Methoden der asynchronen Anwendung
Mariusz Jamro
Das Problem bei dieser Technik ist, dass Sie, wenn Sie später eine nicht asynchrone Version erstellen, Ihren ansonsten bevorzugten "GetUser ()" - Namen nicht verwenden können.
David
3
Dies ist der pragmatische Weg. Das Anhängen von Async an jede Methode mit dem Modifikator async ist nur die ungarische Notation 2019. @David Wenn Sie später eine nicht asynchrone Version hinzufügen, benennen Sie die Methoden um und befolgen Sie die Namenskonvention oder tun Sie dies einfach nicht.
Skrymsli
25

Was ist die Konvention zum Suffixieren von Methodennamen mit "Async"?

Das aufgabenbasierte asynchrone Muster (TAP) schreibt vor, dass Methoden immer ein Task<T>(oder Task) zurückgeben und mit einem asynchronen Suffix benannt werden sollen. Dies ist unabhängig von der Verwendung von async. Beide Task<bool> Connect()und werden gut kompiliert und ausgeführt, aber Sie werden die TAP-Namenskonvention nicht befolgen.asyncTask<bool> Connect()

Sollte die Methode den asyncModifikator enthalten oder reicht sie aus, um nur Task zurückzugeben?

Wenn der Hauptteil der Methode (unabhängig vom Rückgabetyp oder -namen) enthält await, müssen Sie Folgendes verwenden async: und der Compiler teilt Ihnen mit, dass der Operator 'await' nur innerhalb einer asynchronen Methode verwendet werden kann. ... ". Rückgabe Task<T>oder Taskist nicht "genug", um die Verwendung zu vermeiden async. Weitere Informationen finden Sie unter Async (C # -Referenz) .

Dh welche dieser Signaturen sind richtig:

Beides und befolgen Sie die TAP-Konventionen ordnungsgemäß. Sie könnten immer das Schlüsselwort verwenden, aber Sie erhalten eine Compiler-Warnung "Diese asynchrone Methode hat keine 'wait'-Operatoren und wird synchron ausgeführt. ...", wenn der Body dies nicht verwendet .asyncTask<bool> ConnectAsync()Task<bool> ConnectAsync()asyncawait

Nаn
quelle
1
Er bezieht sich darauf, ob Sie "Async" an den Methodennamen anhängen oder nicht, nicht darauf, ob Sie das asyncSchlüsselwort verwenden.
Servy
1
@Servy, ob das Schlüsselwort verwendet asyncwird oder nicht, ist der zweite Teil der Frage.
Corak
2
@Servy Es ist eine zweiteilige Frage. Der erste Teil ist, wie Sie sagten, ob "Async" an den Methodennamen angehängt werden soll oder nicht. Der zweite Teil ist, ob der asyncModifikator verwendet werden soll oder nicht . Siehe auch OP-Beispiele public async Task<bool> ConnectAsync()(mit asyncModifikator) vs public Task<bool> ConnectAsync()(ohne asyncModifikator). Die Methode Name selbst hat das Suffix „Async“ in beiden Fällen.
Corak
2
Es ist keine zweiteilige Frage. Die Frage ist, ob "Async" an Methodennamen von zurückgegebenen Methoden Taskoder Methoden mit angehängten Methoden angehängt werden soll async Task.
Kasperhj
3
@lejon: du solltest die Frage verbessern; das "vs." Code-Schnipsel machen deutlich, dass es sich bei der Frage (in ihrer Gesamtheit) um Async handelt, da dies der einzige Unterschied ist.
Am
11

oder reicht es, dass es nur Task zurückgibt?

Das. Das asyncSchlüsselwort ist hier nicht das eigentliche Problem. Wenn Sie die Asynchronität implementieren, ohne die zu verwendenasync Schlüsselworts , lautet die Methode im allgemeinen Sinne immer noch "Async".

Servieren
quelle
7

Da Taskund Task<T>beide zu erwartende Typen sind, repräsentieren sie einige asynchrone Operation. Oder zumindest sollten sie darstellen.

Sie sollten Asynceiner Methode ein Suffix hinzufügen , das in einigen Fällen (nicht unbedingt allen) keinen Wert zurückgibt, sondern einen Wrapper für einen laufenden Vorgang zurückgibt. Dieser Wrapper ist normalerweise ein Task, aber unter Windows RT kann es sein IAsyncInfo. Folgen Sie Ihrem Bauchgefühl und denken Sie daran, wenn ein Benutzer Ihres Codes das siehtAsync Funktion , weiß, dass der Aufruf dieser Methode vom Ergebnis dieser Methode entkoppelt ist und dass er entsprechend handeln muss.

Beachten Sie, dass es Methoden wie Task.Delayund gibt, Task.WhenAlldie zurückgeben Taskund die noch nicht habenAsync Suffix haben.

Beachten Sie auch, dass es async voidMethoden gibt , die eine asynchrone Methode zum Feuern und Vergessen darstellen , und Sie sollten sich besser darüber im Klaren sein, dass die Methode auf diese Weise erstellt wurde.

Toni Petrina
quelle
6

Ich würde argumentieren, dass es das Async-Suffix verwenden sollte, wenn es eine Task zurückgibt, unabhängig davon, ob die Methode mit dem deklariert ist async Modifikator oder nicht.

Der Grund dafür ist, dass der Name in der Schnittstelle deklariert ist. Die Schnittstelle deklariert den Rückgabetyp a Task. Dann gibt es zwei Implementierungen dieser Schnittstelle, eine Implementierung implementiert sie unter Verwendung des asyncModifikators, die andere nicht.

public interface IFoo
{
    Task FooAsync();
}

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

public class FooB : IFoo
{
    public async Task FooAsync() { /* ... */ }
}
Fred
quelle
Das ist so wahr. Wir verwenden immer und überall Schnittstellen, und Schnittstellen können nicht als asynchron deklariert werden. Die offiziellen Anleitungen zur Verwendung des Async-Suffix erscheinen mir also völlig unsinnig. Ich denke, dass das asynchrone Schlüsselwort nur ein Implementierungsdetail ist, das Teil der Interna der Methode ist und den Namen oder was auch immer extern beeinflussen sollte.
Al Kepp
5

In der asynchronen Programmierung mit asynchronem und warten (C #) bietet Microsoft die folgenden Anleitungen an:

Namenskonvention

Konventionell hängen Sie "Async" an die Namen von Methoden an, die asynchron sind Modifikator haben.

Sie können die Konvention ignorieren, bei der ein Ereignis, eine Basisklasse oder ein Schnittstellenvertrag einen anderen Namen vorschlägt. Beispielsweise sollten Sie allgemeine Ereignishandler nicht umbenennen, z Button1_Click.

Ich finde diese Anleitung unvollständig und unbefriedigend. Bedeutet dies, dass in Abwesenheit des asyncModifikators diese Methode Connectanstelle von benannt werden sollte ConnectAsync?

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

Das glaube ich nicht. Wie in der angegebenen prägnanten Antwort von @Servy und der weiteren detaillierten Antwort von @Luke Puplett , glaube ich , dass es angemessen ist , und in der Tat zu erwarten , dass diese Methode soll benannt werden ConnectAsync(weil es eine awaitable zurückzugibt). Zur weiteren Unterstützung wird @John Skeet in dieser Antwort auf eine andere Frage Asyncunabhängig vom Vorhandensein des asyncModifikators an den Methodennamen angehängt .

Betrachten Sie abschließend bei einer anderen Frage diesen Kommentar von @Damien_The_Unbeliever :

async/awaitsind Implementierungsdetails Ihrer Methoden. Für Ihre Anrufer spielt es keine Rolle, ob Ihre Methode deklariert ist async Task Method()oder nur . (Tatsächlich können Sie zu einem späteren Zeitpunkt zwischen diesen beiden wechseln, ohne dass dies als brechende Änderung angesehen wird.)Task Method()

Daraus schließe ich, dass es die asynchrone Natur der Methode ist , die vorschreibt, wie sie benannt werden soll. Der Benutzer der Methode weiß nicht einmal, ob der asyncModifikator in seiner Implementierung verwendet wird (ohne den C # -Quellcode oder CIL).

DavidRR
quelle