返回 IAsyncEnumerable 的方法是否有明确的命名约定?

Is there a definitive naming convention for methods returning IAsyncEnumerable?

在 C# 5 为异步编程引入 asyncawait 模型后,C# 社区达成了一个命名约定,为返回可等待类型的方法添加 "Async" 后缀,像这样:

interface Foo
{
   Task BarAsync();
}

许多静态代码分析器(基于 Roslyn 的和非基于 Roslyn 的)已经被编写为在检测异步编程周围的代码异味时依赖于此命名约定。

既然 C# 8 引入了异步枚举的概念,它们本身是不可等待的,但可以与 await foreach 结合使用,似乎有两种命名方法返回 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# 语言团队、.NET 基金会或其他权威机构),例如 C# 5 命名约定如何明确标准化且不留意见-基于程序员的判断?

Async 后缀甚至关键字 async 只是样板文件,除了一些向后兼容性参数外没有任何意义。 C# 拥有区分这些函数的所有信息,就像它在 return IEnumerable 方法中区分 yield 一样,正如您所知,您不需要添加任何东西,例如 enumerable 或一些此类方法的其他关键字。

你需要添加 Async 后缀只是因为 C# 会抱怨重载,所以本质上这两者是相同的并且它们根据多态性的所有规则做同样的事情(如果我们不是考虑到故意腐败行为的人为因素):

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

但是你不能这样写,因为编译器会报错。但是,嘿!它会吞下这个怪物:

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# 量子计算机方法扩展(幻想,呵呵)将在不同的范例中运行,我们可能需要另一个后缀,如 Quant 之类的,否则 C# 会报错。这将是完全相同的方法,但它会以另一种方式工作。就像多态一样。就像接口一样。

它不是异步方法,因此名称不应以“Async”结尾。该方法后缀是一种约定,表明应该等待该方法,或者将结果作为任务处理。

我认为一个正常的集合返回名称是合适的。 GetFoos(),或类似的。

没有比 .NET 团队已经做的更好的指南了:

  • ChannelReader.ReadAllAsync returns 一个 IAsyncEnumerable<T>
  • 在 EF Core 3 中,结果通过调用 AsAsyncEnumerable()
  • 作为 IAsyncEnumerable 返回
  • 在System.Linq.Async中,ToAsyncEnumerable()将IEnumerables、Tasks和Observables转换为IAsyncEnumerables
  • System.Linq.Async 中的所有其他运算符保留其名称。没有 SelectAsyncSelectAsAsyncEnumerable,只有 Select

在所有情况下,很清楚该方法的结果是什么。在所有情况下,方法的结果都需要等待 await foreach 才能使用。

因此 真实 准则保持不变 - 确保名称使行为清晰:

  • 当名称已经明确时,例如AsAsyncEnumerable()ToAsyncEnumerable(),则无需添加任何后缀。
  • 在其他情况下,添加 Async 后缀,以便 开发人员 知道他们需要 await foreach 结果。

代码分析器和生成器并不真正关心方法的名称,它们通过检查代码本身来检测异味。代码分析器会告诉您忘记等待任务或 await foreachIAsyncEnumerable,无论您如何调用方法和变量。生成器可以简单地使用反射来检查 IAsyncEnumerable 并发出 await foreach

检查名称的是样式 分析器。他们的工作是确保代码使用一致的风格,以便 开发人员 可以理解代码。风格分析器会告诉你一个方法不符合你选择的风格。该风格可能是团队的或普遍接受的风格指南。

当然,每个人都知道私有实例字段的通用前缀是_ :)

根据 .Net 团队的说法,答案似乎是返回 IAsyncEnumerable<> 的方法应该以“Async”结尾。

Stephen Toub,其中一位 .Net 运行时超级巨星,在这个 GitHub 问题上针对 C# 流的 public API 如此陈述: https://github.com/dotnet/runtime/issues/27547#issuecomment-478384285

Yes [the method names will end in “Async”]. The most common consumption will be via await foreach, and it serves as a similar visual indicator of where asynchrony is involved, allows for differentiation from sync enumerables, etc.