Existe-t-il une convention de dénomination définitive pour les méthodes renvoyant IAsyncEnumerable?

10

Après que C # 5 a introduit le modèle asyncet awaitpour la programmation asynchrone, la communauté C # est arrivée à une convention de dénomination pour ajouter un suffixe "Async" aux méthodes renvoyant un type attendu, comme ceci:

interface Foo
{
   Task BarAsync();
}

De nombreux analyseurs de code statique (basés sur Roslyn et non basés sur Roslyn) ont depuis été écrits pour dépendre de cette convention de dénomination lors de la détection de l'odeur de code autour de la programmation asynchrone.

Maintenant que C # 8 a introduit le concept des énumérables asynchrones, qui eux-mêmes ne sont pas attendus mais peuvent être utilisés en conjonction avec await foreach, il semble y avoir deux options pour les méthodes de nommage renvoyant IAsyncEnumerable:

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

ou

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

Existe-t-il une directive de convention de dénomination définitive (de l'équipe du langage C #, de la Fondation .NET ou d'autres autorités) concernant les options ci-dessus, comme la façon dont la convention de dénomination C # 5 a été uniformisée sans ambiguïté et n'a pas été laissée à l'appréciation des programmeurs basée sur l'opinion.

hwaien
la source
2
L'option 2 semble correcte ici, car vous exécutez ce code de manière asynchrone (marquez la méthode comme asyncet utilisez à l' awaitintérieur), et l' exemple msdn montre la même chose
Pavel Anikhouski
1
En réponse à la fermeture de la question, j'ai édité la question pour demander s'il existe une convention de dénomination définitive et pour donner un exemple de la façon dont une convention de dénomination définitive en C # 5 a abouti à des développements dans l'analyse de code statique. Par conséquent, avoir C # 8 suivre ce modèle entraînera des améliorations mesurables de la qualité du code au-delà des préférences de codage basées sur l'opinion.
hwaien
.NET Core lui-même utilise le Asyncsuffixe pour les méthodes qui retournent IAsyncEnumerable, par exemple ChannelReader.ReadAllAsync . Dans d'autres cas, par exemple dans EF Core, AsAsyncEnumerable()est utilisé, ce qui est déjà clair
Panagiotis Kanavos
Des directives de dénomination existent pour faciliter la compréhension du code par les humains. Si le but du code n'est pas clair, ajoutez le suffixe.
Panagiotis Kanavos

Réponses:

1

Il n'y a pas de meilleure directive que ce que les équipes .NET font déjà :

  • ChannelReader.ReadAllAsync renvoie unIAsyncEnumerable<T>
  • Dans EF Core 3, les résultats sont renvoyés sous la forme IAsyncEnumerabled' un en appelant AsAsyncEnumerable ()
  • Dans System.Linq.Async, ToAsyncEnumerable () convertit IEnumerables, Tasks et Observables en IAsyncEnumerables
  • Tous les autres opérateurs System.Linq.Asyncconservent leur nom. Il n'y a pas SelectAsyncou SelectAsAsyncEnumerablejuste Select.

Dans tous les cas, le résultat de la méthode est clair. Dans tous les cas, les résultats de la méthode doivent être attendus await foreachavant de pouvoir être utilisés.

Ainsi, la véritable ligne directrice reste la même - assurez - vous que le nom rend le comportement clair :

  • Lorsque le nom est déjà clair, par exemple avec AsAsyncEnumerable()ou ToAsyncEnumerable(), il n'est pas nécessaire d'ajouter de suffixe.
  • Dans d'autres cas, ajoutez le Asyncsuffixe pour que les développeurs sachent qu'ils ont besoin await foreachdu résultat.

Les analyseurs et générateurs de code ne se soucient pas vraiment du nom des méthodes, ils détectent les odeurs en inspectant le code lui-même. Un analyseur de code vous dira que vous avez oublié d'attendre une tâche ou await foreachun IAsyncEnumerablepeu importe comment vous appelez les méthodes et les variables. Un générateur peut simplement utiliser la réflexion pour vérifier IAsyncEnumerableet émettreawait foreach

Ce sont les analyseurs de style qui vérifient les noms. Leur travail consiste à s'assurer que le code utilise un style cohérent afin que les développeurs puissent comprendre le code. L'analyseur de style vous dira qu'une méthode ne suit pas le style que vous avez choisi. Ce style peut être celui de l'équipe ou un guide de style communément accepté.

Et bien sûr, tout le monde sait que le préfixe commun pour les champs d'instance privés est _:)

Panagiotis Kanavos
la source
Merci d'avoir rappelé l' ChannelReader.ReadAllAsyncexemple. Étant donné que cela vient directement de l'équipe .NET, il est difficile de se tromper en suivant le mouvement.
hwaien
Les packages d'analyseurs sont souvent livrés avec un sac de mélange d'analyseurs de style et non-style. Par exemple, le package Microsoft.VisualStudio.Threading.Analyzers possède un analyseur de style qui vérifie la convention de dénomination (VSTHRD200) et un analyseur sans style qui détecte les situations dangereuses pouvant potentiellement conduire à un blocage (VSTHRD111). Pour référencer le package afin de profiter de VSTHRD111, un projet se retrouverait également avec VSTHRD200.
hwaien
2

Ce n'est pas une méthode asynchrone, donc le nom ne doit pas se terminer par 'Async'. Ce suffixe de méthode est une convention qui rend évident que la méthode doit être attendue ou le résultat traité comme une tâche.

Je pense qu'un nom de retour de collection normal est approprié. GetFoos (), ou similaire.

David Browne - Microsoft
la source
Ce sont tous des arguments pour utiliser le Asyncsuffixe. Le suffixe est utilisé dans .NET Core lui-même: ChannelReader.ReadAllAsync renvoie un IAsyncEnumerable. Un IAsyncEnumerabledoit être utilisé avec await foreach, donc le suffixe est logique.
Panagiotis Kanavos
1

Le suffixe asynchrone et même le mot async- clé ne sont qu'un passe-partout, il n'a pas de sens autre que certains arguments de compatibilité descendante. C # a toutes les informations pour distinguer ces fonctions comme il le fait yielddans les méthodes qui retournent IEnumerable, comme vous le savez, vous n'avez pas besoin d'ajouter quoi que ce soit comme enumerableou un autre mot-clé sur ces méthodes.

Vous devez ajouter le suffixe Async juste parce que C # se plaindra de surcharges, donc essentiellement ces deux sont identiques et ils font la même chose selon toutes les règles du polymorphisme (si nous ne tenons pas compte du facteur humain du comportement de corruption intime):

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

Mais vous ne pouvez pas l'écrire comme ça car le compilateur se plaindra. Mais salut! Il va avaler cette monstruosité:

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

et même ceci:

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

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

Alors c'est ça. Vous ajoutez Async juste pour arrêter le compilateur pour vous plaindre. Pas pour clarifier les choses. Pas pour les améliorer. S'il n'y a pas de plainte - aucune raison de l'ajouter, alors dans votre cas, j'éviterai cela, à moins que cela ne devienne Task<IAsyncEnumerable>.

PS: Juste pour mieux comprendre l'image entière ici - si dans le futur quelqu'un ajoutera des extensions de méthode informatique quantique C (fantazy, hein) qui fonctionneront dans différents paradigmes, nous aurons probablement besoin d'un autre suffixe comme Quant ou quelque chose, parce que C # se plaindra autrement. Ce sera exactement la même méthode, mais cela fonctionnera d'une autre manière. Tout comme le polymorphisme. Tout comme les interfaces.

eocron
la source
1
Désolé, mais je sens que je ne suis pas d'accord, ce n'est pas vraiment une approche différente de faire la même chose - c'est plus - c'est censé être appelé différemment et le résultat doit être collecté différemment. C'est la plus grande différence entre asynchrone et non asynchrone. Je crois fermement que l'ajout de l'async est pour plus de clarté. Je pense que c'est également la recommandation de Microsoft.
madoxdev
Ouais, je suis d'accord mais pas d'accord. Tout comme dans List, vous avez une fonction de tri simple et non "QuickSort", "RadixSort", "BubbleSort", "MixOfSorts". Ils sont différents, utilisés par la portée mais n'ont évidemment pas besoin d'être clarifiés autrement qu'à des fins de débogage (je veux dire que vous ne vous souciez d'eux que lorsqu'ils ont un impact sur les performances / ressources). Dans ce cas, DoThing et DoThingAsync sont dans la même situation que tout autre algorithme basé sur un modèle, ils n'ont pas besoin de clarification, il est pris en charge ou non, et utile uniquement pour le débogage.
eocron
C'est différent, async signifie - appelant heu assurez-vous de gérer cela correctement. Le fait de se retirer de Task n'est pas suffisant pour dire que la méthode est vraiment Async. L'appelant doit savoir attendre ou utiliser GetAwaiter (). GetResilt () pour obtenir la même sortie. C'est différent, pas la façon dont les choses sont faites à l'intérieur.
madoxdev
C'est entièrement le but des analyseurs de code et du compilateur. IntellySense peut facilement le signaler pour vous, pas besoin d'utiliser le cerveau des développeurs pour mettre en évidence ou même bannir l'utilisation de la méthode de synchronisation sur async one en code asynchrone. D'après mon expérience lors de l'utilisation d'Async, j'ai rarement besoin d'utiliser GetResult (), mais j'ai souvent besoin de polluer toute la base de code avec Async.
eocron
Comme quelqu'un l'a marqué - c'est l'opinion. Nous pouvons continuer à croire en ce que nous croyons et être heureux :)
madoxdev