IAsyncEnumerableを返すメソッドの明確な命名規則はありますか?


10

C#5 が非同期プログラミングのasyncおよびawaitモデルを導入した後、C#コミュニティは、次のように、待機可能な型を返すメソッドに「非同期」サフィックスを追加する命名規則に到達しました。

interface Foo
{
   Task BarAsync();
}

多くの静的コードアナライザー(Roslynベースと非Roslynベースの両方)は、非同期プログラミングに関するコードのにおいを検出するときに、この命名規則に依存するように作成されています。

C#8が非同期列挙型の概念を導入したので、それ自体は待機できませんが、と組み合わせて使用​​できます。await foreachメソッドに名前を付けるための2つのオプションが返されるようIAsyncEnumerableです。

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

または

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

上記のオプションに関して、C#5の命名規則が明確に標準化され、プログラマーの意見に基づく判断に委ねられていないなど、明確な命名規則のガイドライン(C#言語チーム、.NET Foundation、またはその他の機関による)はありましたか?


2
オプション2はここで正しく聞こえます。これは、このコードを非同期に実行する(メソッドをasとしてマークして内部asyncで使用するawait)ためです。msdnサンプルでも同じです
Pavel Anikhouski

1
質問の締めくくりに応じて、明確な命名規則が存在するかどうかを尋ねる質問を編集し、C#5の明確な命名規則が静的コード分析の開発にどのように貢献したかを示す例を示しました。したがって、C#8にそのパターンを適用すると、オピニオンベースのコーディング設定を超えて、測定可能なコード品質が向上します。
ハワイアン

.NET Core自体は、たとえばChannelReader.ReadAllAsyncAsyncなど、返すメソッドにサフィックスを使用します。他のケースでは、例えばEF Coreで、すでに明確に使用されていますIAsyncEnumerableAsAsyncEnumerable()
Panagiotis Kanavos

命名ガイドラインは、人間がコードを理解しやすくするために存在します。コードの目的が明確でない場合は、接尾辞を追加してください。
Panagiotis Kanavos

回答:


1

.NETチームが既に行っていることよりも優れたガイドラインはありません。

  • ChannelReader.ReadAllAsyncは、IAsyncEnumerable<T>
  • EF Core 3では、AsAsyncEnumerable()をIAsyncEnumerable呼び出すと結果がとして返されます
  • System.Linq.Asyncでは、ToAsyncEnumerable()は IEnumerables、Tasks、ObservablesをIAsyncEnumerablesに変換します
  • の他のすべての演算子はSystem.Linq.Async名前を保持します。SelectAsyncまたはSelectAsAsyncEnumerableはありませんSelect

すべての場合において、メソッドの結果が何であるかは明らかです。すべての場合において、メソッドの結果は、await foreach使用する前に待つ必要があります。

したがって、実際のガイドラインは同じままです- 名前が動作を明確にすることを確認してください:

  • 名前がすでに明確である場合(例:AsAsyncEnumerable()または)ToAsyncEnumerable()、サフィックスを追加する必要はありません。
  • それ以外の場合は、Asyncサフィックスを追加して、開発者await foreach結果を知る必要があることを確認します。

コードアナライザーとジェネレーターは、メソッドの名前を実際には気にせず、コード自体を検査することで臭いを検出します。コードアナライザを使用すると、タスクまたは待つのを忘れていることを教えてくれますあなたはメソッドや変数を呼び出す方法に関係なく。ジェネレーターは単純にリフレクションを使用してチェックして放出することができますawait foreachIAsyncEnumerableIAsyncEnumerableawait foreach

名前をチェックするのはスタイルアナライザーです。彼らの仕事は、開発者がコードを理解できるように、コードが一貫したスタイルを使用するようにすることです。スタイルアナライザーは、メソッドが選択したスタイルに従っていないことを通知します。そのスタイルは、チームまたは一般的に受け入れられているスタイルガイドです。

そしてもちろん、プライベートインスタンスフィールドの一般的なプレフィックスが_:)であることは誰もが知っています。


ChannelReader.ReadAllAsync例を指摘していただきありがとうございます。これは.NETチームから直接取得されたものであるため、次の訴訟で失敗することはほとんどありません。
ハワイアン

アナライザーパッケージには、多くの場合、スタイルアナライザーと非スタイルアナライザーの混合バッグが付属しています。たとえば、Microsoft.VisualStudio.Threading.Analyzersパッケージには、命名規則をチェックするスタイルアナライザー(VSTHRD200)と、デッドロックにつながる可能性のある危険な状況を検出する非スタイルアナライザー(VSTHRD111)があります。パッケージを参照してVSTHRD111を利用するには、プロジェクトもVSTHRD200になります。
ハワイアン

2

これは非同期メソッドではないため、名前の末尾を「Async」にしないでください。そのメソッドのサフィックスは、メソッドが待機する必要があること、または結果がタスクとして処理されることを明確にするための規則です。

通常のコレクションを返す名前が適切だと思います。GetFoos()など。


これらはすべて、サフィックス使用するための引数ですAsync。接尾辞は.NET Core自体で使用されます 。ChannelReader.ReadAllAsyncIAsyncEnumerableを返します。IAsyncEnumerable一緒に使用する必要があるawait foreachので、サフィックスは理にかなっています。
Panagiotis Kanavos

1

非同期のサフィックスとキーワードでさえasync、単なる定型文であり、後方互換性に関するいくつかの引数以外は意味がありません。C#には、yieldを返すメソッドで区別するのと同じように、これらの関数を区別するためのすべての情報がIEnumerableありenumerableます。

C#がオーバーロードについて文句を言うからといって、非同期のサフィックスを追加する必要があります。したがって、基本的にこれら2つは同一であり、ポリモーフィズムのすべてのルールによって同じことを行います(意図的に破損する動作の人的要因を考慮していない場合)。

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

しかし、コンパイラは文句を言うので、このように書くことはできません。しかし、ちょっと!それはこの1つの怪物を飲み込むでしょう:

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

そしてこれさえ:

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

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

これでおしまいです。コンパイラーが文句を言うのをやめるためだけにAsyncを追加します。物事を明確にするためではありません。それらを良くしないために。文句がない場合-それを追加する理由はないので、あなたの場合、それがにならない限り、私はこれを避けますTask<IAsyncEnumerable>

PS:ここで全体像をよりよく理解するために-将来誰かが異なるパラダイムで動作するC#量子コンピューターメソッド拡張機能(fantazy、huh)を追加する場合、C#のため、おそらくQuantまたは何かのような別のサフィックスが必要になりますそうでなければ文句を言うでしょう。まったく同じ方法ですが、別の方法で機能します。ポリモーフィズムと同じように。インターフェイスと同じように。


1
申し訳ありませんが、私はそうは思わない、それは同じことをすることの実際に異なるアプローチではない-それ;さらに-それは異なる方法で呼び出され、結果が異なる方法で収集されることが期待されます。これが非同期と非同期の最大の違いです。非同期を追加することは明確にするためだと強く信じています。これもマイクロソフトの推奨だと思います。
madoxdev

ええ、同意しますが同意しません。リストと同じように、「QuickSort」、「RadixSort」、「BubbleSort」、「MixOfSorts」ではなく、単純なソート関数があります。それらは異なり、スコープによって使用されますが、デバッグ以外の目的で明確にする必要がないことは明らかです(つまり、パフォーマンス/リソースに影響を与える場合にのみ、それらを気にする必要があります)。この場合、DoThingとDoThingAsyncは他のテンプレート化されたアルゴリズムと同じ状況にあり、それらは説明を必要とせず、サポートされているかどうかにかかわらず、デバッグにのみ役立ちます。
eocron、

つまり、非同期とは、呼び出し側がこれを正しく処理することを確認することを意味します。タスクを再起動するという事実は、メソッドが本当に非同期であると言うには十分ではありません。呼び出し元は、同じ出力を取得するために待機するかGetAwaiter()。GetResilt()を使用することを知っている必要があります。それは物事が内部で行われる方法とは異なります。
madoxdev

これは完全にコードアナライザーとコンパイラーの目的です。IntellySenseはこれを簡単に指摘できます。非同期コードで非同期メソッドよりも同期メソッドの使用を強調表示したり、消したりするために開発者の頭脳を使用する必要はありません。Asyncを使用するときの私の経験から、GetResult()を使用する必要はめったにありませんが、Asyncを使用してコードベース全体を頻繁に汚染する必要があります。
eocron

誰かがそれをマークしたように-それは意見です。私たちは自分の信じていることを信じ続け、幸せになることができます:)
madoxdev '21 / 12/19
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.