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


10

在C#5引入async和之后await用于异步编程模型之后,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 Foundation或其他机构),例如C#5命名约定是如何明确标准化的,而不是由程序员根据意见进行判断的?


2
选项2在这里听起来是正确的,因为您异步运行此代码(将方法标记为,asyncawait在内部使用),而msdn示例显示了相同的内容
Pavel Anikhouski

1
为了回答这个问题,我编辑了问题,以询问是否存在确定的命名约定,并举例说明C#5中的确定的命名约定如何导致静态代码分析的发展。因此,使C#8遵循该模式将带来可衡量的代码质量改进,而不仅仅是基于观点的编码首选项。
夏威夷

.NET Core本身将Async后缀用于返回的方法IAsyncEnumerable,例如ChannelReader.ReadAllAsync。在其他情况下,例如,在EF Core AsAsyncEnumerable()中使用的情况已经很清楚
Panagiotis Kanavos 19/12/23

存在命名准则以使人们更容易理解代码。如果代码的目的不明确,请添加后缀。
Panagiotis Kanavos

Answers:


1

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

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

因此,真正的指导方针保持不变- 确保名称明确表明行为

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

代码分析器和生成器实际上并不关心方法的名称,它们通过检查代码本身来检测气味。代码分析器会告诉你,你忘了等待任务或await foreach一个IAsyncEnumerable无论你如何调用的方法和变量。生成器可以简单地使用反射来检查IAsyncEnumerable和发射await foreach

它的风格分析仪,它检查名称。他们的工作是确保代码使用一致的样式,以便开发人员可以理解代码。样式分析器将告诉您,一种方法未遵循您选择的样式。该样式可能是团队的样式或普遍接受的样式指南。

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


感谢您指出ChannelReader.ReadAllAsync示例。由于这直接来自.NET团队,因此很难照做。
夏威夷

分析仪套件通常随附有样式分析仪和非样式分析仪的混合袋。例如,Microsoft.VisualStudio.Threading.Analyzers程序包具有一个样式分析器(用于检查命名约定(VSTHRD200))和一个非样式分析器,用于检测可能导致死锁的危险情况(VSTHRD111)。为了引用该软件包以利用VSTHRD111,一个项目也将以VSTHRD200结尾。
夏威夷

2

这不是异步方法,因此名称不应以“异步”结尾。该方法后缀是一种约定,使您可以明显地等待该方法,或者将结果作为Task处理。

我认为使用正常的集合返回名称是合适的。GetFoos()或类似的东西。


所有这些都是争论使用Async后缀。后缀在.NET Core本身中使用: ChannelReader.ReadAllAsync返回IAsyncEnumerable。一个IAsyncEnumerable具有与其一起使用await foreach,所以后缀是有意义的。
Panagiotis Kanavos

1

异步后缀甚至关键字async只是样板,除了一些向后兼容性参数之外,它没有任何意义。C#拥有区分这些功能的所有信息,就像它yield在return的方法中的区分一样IEnumerable,正如您所知,您不需要添加任何内容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);
}

就是这样。您添加异步只是为了阻止编译器抱怨。不澄清事情。不要让他们变得更好。如果没有抱怨-没有理由添加它,那么在您的情况下,我会避免这种情况,除非它成为Task<IAsyncEnumerable>

PS:只是为了更好地理解整个图片-如果将来某个时候添加了将以不同范例运行的C#量子计算机方法扩展(幻想,呵呵),我们可能将需要另一个后缀,例如Quant之类的东西,因为C#否则会抱怨。这将是完全相同的方法,但是它将以另一种方式进行工作。就像多态一样。就像接口一样。


1
抱歉,但是我不同意,做同一件事不是真的不同方法,更多的是,它的名称应有所不同,而结果将有所不同。那是异步和非异步之间的最大区别。我坚信添加异步是为了清楚起见。我认为这也是微软的建议。
madoxdev

是的,我同意但不同意。就像在列表中一样,您具有简单的排序功能,而没有“ QuickSort”,“ RadixSort”,“ BubbleSort”,“ MixOfSorts”。它们是不同的,按作用域使用,但显然除了调试目的外,不需要澄清(我的意思是,仅在它们影响性能/资源时才关心它们)。在这种情况下,DoThing和DoThingAsync处于与其他任何模板化算法相同的情况,它们不需要澄清,是否受支持,仅对调试有用。
eocron '19

那就不同了,异步意味着-调用方请确保您正确处理此问题。淘汰Task的事实不足以说该方法是真正的Async。呼叫者必须知道等待或使用GetAwaiter()。GetResilt()来获得相同的输出。这与内部的处理方式不同。
madoxdev

这完全是代码分析器和编译器的目的。IntellySense可以轻松地为您指出这一点,无需使用开发人员的大脑来突出显示甚至消除使用同步方法而不是异步代码中的同步方法。根据我使用异步的经验,我很少需要使用GetResult(),但是我经常需要使用异步来污染整个代码库。
eocron '19

正如有人标记的那样-这是意见。我们可以继续相信我们所相信的并感到幸福:)
madoxdev
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.