Esiste una convenzione di denominazione definitiva per i metodi che restituiscono IAsyncEnumerable?


10

Dopo che C # 5 ha introdotto il modello asynce awaitper la programmazione asincrona, la comunità C # è arrivata a una convenzione di denominazione per aggiungere un suffisso "Async" ai metodi che restituiscono un tipo attendibile, come questo:

interface Foo
{
   Task BarAsync();
}

Da allora, molti analizzatori di codice statico (sia basati su Roslyn che non basati su Roslyn) sono stati scritti in modo da dipendere da questa convenzione di denominazione quando rilevano odore di codice attorno alla programmazione asincrona.

Ora che C # 8 ha introdotto il concetto di enumerabili asincroni, che a loro volta non sono prevedibili ma che possono essere utilizzati insieme await foreach, sembrano esserci due opzioni per restituire i metodi di denominazione IAsyncEnumerable:

interface Foo
{
    // No "Async" suffix, indicating that the return value is not awaitable.
    IAsyncEnumerable<T> Bar<T>();
}

o

interface Foo
{
    // With "Async" suffix, indicating that the overall logic is asynchronous.
    IAsyncEnumerable<T> BarAsync<T>();
}

C'è stata una linea guida per la convenzione di denominazione definitiva (dal team linguistico C #, .NET Foundation o altre autorità) riguardo alle opzioni sopra, come il modo in cui la convenzione di denominazione C # 5 è stata standardizzata in modo inequivocabile e non lasciata al giudizio dei programmatori basato sull'opinione?


2
L'opzione 2 suona bene qui, dato che esegui questo codice in modo asincrono (contrassegna il metodo as asynce usa awaitdentro), e l' esempio
msdn

1
In risposta alla chiusura della domanda, ho modificato la domanda per chiedere se esiste una convenzione di denominazione definitiva e per dare un esempio di come una convenzione di denominazione definitiva in C # 5 abbia portato a sviluppi nell'analisi del codice statico. Pertanto, facendo in modo che C # 8 segua questo modello, si otterranno miglioramenti della qualità del codice misurabili oltre alle preferenze di codifica basate sull'opinione.
hwaien,

.NET Core stesso utilizza il Asyncsuffisso per i metodi che restituiscono IAsyncEnumerable, ad esempio ChannelReader.ReadAllAsync . In altri casi, ad esempio in EF Core, AsAsyncEnumerable()viene utilizzato che è già chiaro
Panagiotis Kanavos,

Esistono linee guida per la denominazione per facilitare la comprensione del codice da parte dell'uomo. Se lo scopo del codice non è chiaro, aggiungi il suffisso.
Panagiotis Kanavos il

Risposte:


1

Non esiste una linea guida migliore di quella che fanno già i team .NET :

  • ChannelReader.ReadAllAsync restituisce unIAsyncEnumerable<T>
  • In EF Core 3, i risultati vengono restituiti come un IAsyncEnumerablechiamando AsAsyncEnumerable ()
  • In System.Linq.Async, ToAsyncEnumerable () converte IEnumerables, Task e Observables in IAsyncEnumerables
  • Tutti gli altri operatori System.Linq.Asyncmantengono i loro nomi. Non c'è SelectAsynco SelectAsAsyncEnumerable, solo Select.

In tutti i casi, è chiaro quale sia il risultato del metodo. In tutti i casi, i risultati del metodo devono essere attesi await foreachprima di poter essere utilizzati.

Quindi la vera linea guida rimane la stessa - assicurati che il nome chiarisca il comportamento :

  • Quando il nome è già chiaro, ad esempio con AsAsyncEnumerable()o ToAsyncEnumerable(), non è necessario aggiungere alcun suffisso.
  • In altri casi, aggiungi il Asyncsuffisso in modo che gli sviluppatori sappiano di aver bisogno await foreachdel risultato.

Gli analizzatori di codice e i generatori non si preoccupano davvero dei nomi dei metodi, rilevano gli odori ispezionando il codice stesso. Un analizzatore di codice vi dirà che avete dimenticato di attendere un'attività o di await foreachun IAsyncEnumerablenon importa come si chiamano i metodi e le variabili. Un generatore può semplicemente usare la riflessione per verificare IAsyncEnumerableed emettereawait foreach

Sono gli analizzatori di stile che controllano i nomi. Il loro compito è garantire che il codice usi uno stile coerente in modo che gli sviluppatori possano comprenderlo. L'analizzatore di stile ti dirà che un metodo non segue lo stile che hai scelto. Quello stile può essere la guida di stile della squadra o comunemente accettata.

E, naturalmente, tutti sanno che il prefisso comune per i campi delle istanze private è _:)


Grazie per aver sottolineato l' ChannelReader.ReadAllAsyncesempio. Dal momento che questo proviene direttamente dal team .NET, è difficile sbagliare seguendo l'esempio.
hwaien,

I pacchetti di analizzatori vengono spesso forniti con un mix bag di analizzatori di stile e non di stile. Ad esempio, il pacchetto Microsoft.VisualStudio.Threading.Analyzers ha un analizzatore di stile che controlla la convenzione di denominazione (VSTHRD200) e un analizzatore non di stile che rileva situazioni pericolose che possono potenzialmente portare a deadlock (VSTHRD111). Per fare riferimento al pacchetto per sfruttare VSTHRD111, un progetto finirebbe anche con VSTHRD200.
hwaien,

2

Non è un metodo asincrono, quindi il nome non deve terminare in "Asincrono". Tale suffisso di metodo è una convenzione per rendere evidente che il metodo dovrebbe essere atteso o il risultato gestito come Task.

Penso che un normale nome che ritorni a raccolta sia appropriato. GetFoos () o simile.


Tutti questi sono argomenti per l' utilizzo del Asyncsuffisso. Il suffisso viene utilizzato in .NET Core stesso: ChannelReader.ReadAllAsync restituisce un IAsyncEnumerable. Un IAsyncEnumerabledeve essere usato con await foreach, quindi il suffisso ha senso.
Panagiotis Kanavos il

1

Suffisso asincrono e persino la parola chiave asyncè solo un esempio, non ha senso oltre ad alcuni argomenti di compatibilità all'indietro. C # ha tutte le informazioni per distinguere quelle funzioni così come si distingue yieldnei metodi che restituiscono IEnumerable, poiché sai che non è necessario aggiungere nulla di simile enumerableo qualche altra parola chiave su tali metodi.

Devi aggiungere il suffisso Async solo perché C # si lamenterà dei sovraccarichi, quindi essenzialmente quei due sono identici e fanno la stessa cosa da tutte le regole del polimorfismo (se non stiamo prendendo in considerazione il fattore umano di comportamento corrotto intenzionalmente):

public interface IMyContract
{
    Task<int> Add(int a, int b);
    int Add(int a, int b);
}

Ma non puoi scriverlo così perché il compilatore si lamenterà. Ma hey! Inghiottirà questa mostruosità:

public interface IMyContract : IEnumerable<int>, IEnumerable<long>
{
}

e anche questo:

public interface IMyContractAsync
{
    Task<int> Add(int a, int b);
}

public interface IMyContract : IMyContractAsync
{
    int Add(int a, int b);
}

Quindi eccolo. Aggiungi Async solo per interrompere il compilatore per lamentarti. Non chiarire le cose. Non per renderli migliori. Se non ci sono lamentele, non c'è motivo di aggiungerlo, quindi nel tuo caso lo eviterò, a meno che non diventiTask<IAsyncEnumerable> .

PS: Solo per una migliore comprensione dell'intera immagine qui - se in futuro qualcuno aggiungerà estensioni del metodo del computer quantistico C # (fantazy, huh) che opereranno in paradigmi diversi, probabilmente avremo bisogno di un altro suffisso come Quant o qualcosa del genere, perché C # si lamenterà altrimenti. Sarà esattamente lo stesso metodo ma farà il suo lavoro in un altro modo. Proprio come fanno i polimorfismi. Proprio come fanno le interfacce.


1
Mi dispiace, ma mi sento in disaccordo, non è un approccio molto diverso nel fare la stessa cosa - è più - si prevede che sia chiamato in modo diverso e risulti essere raccolto in modo diverso. Questa è la differenza più grande tra asincrono e non asincrono. Credo fermamente che l'aggiunta dell'asincrono sia per chiarezza. Penso che questa sia anche la raccomandazione di Microsoft.
madoxdev,

Sì, sono d'accordo ma non sono d'accordo. Proprio come in Elenco hai una semplice funzione di ordinamento e non "QuickSort", "RadixSort", "BubbleSort", "MixOfSorts". Sono diversi, usati dall'ambito ma ovviamente non hanno bisogno di chiarimenti se non per scopi di debug (intendo che ti interessano solo quando incidono sulle prestazioni / risorse). In questo caso DoThing e DoThingAsync si trovano nella stessa situazione di qualsiasi altro algoritmo basato su modelli, non hanno bisogno di chiarimenti, sono supportati o meno e utili solo per il debug.
eocron,

Quello è diverso, asincrono significa - heu chiamante assicurati di gestirlo correttamente. Il fatto di ricominciare Task non è sufficiente per dire che il metodo è veramente Asincrono. Il chiamante deve sapere di attendere o utilizzare GetAwaiter (). GetResilt () per ottenere lo stesso output. Non è diverso il modo in cui le cose vengono fatte all'interno.
madoxdev,

Questo è interamente scopo degli analizzatori di codice e del compilatore. IntellySense può facilmente evidenziarlo per te, non è necessario utilizzare il cervello degli sviluppatori per evidenziare o addirittura bandire l'uso del metodo di sincronizzazione su uno asincrono in codice asincrono. Dalla mia esperienza con Async, raramente ho bisogno di usare GetResult (), ma spesso ho bisogno di inquinare l'intera base di codice con Async.
eocron,

Come qualcuno l'ha segnato - è opinione. Possiamo continuare a credere in ciò in cui crediamo ed essere felici :)
madoxdev il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.