在方法名称中使用"Async"后缀是否取决于是否使用了"async"修饰符?

kas*_*rhj 90 .net c# naming naming-conventions async-await

使用"Async"后缀方法名称的约定是什么?

"Async"后缀是否应附加到使用async修饰符声明的方法?

public async Task<bool> ConnectAsync()
Run Code Online (Sandbox Code Playgroud)

或者该方法刚刚返回Task<T>还是足够Task

public Task<bool> ConnectAsync()
Run Code Online (Sandbox Code Playgroud)

Luk*_*ett 109

我认为即使从Microsoft文档中,事实也是模棱两可的:

在Visual Studio 2012和.NET Framework 4.5中,任何使用async关键字(Async在Visual Basic中)归属的方法都被视为异步方法,C#和Visual Basic编译器执行必要的转换以使用TAP异步实现该方法.异步方法应该返回一个Task或一个Task<TResult>对象.

http://msdn.microsoft.com/en-us/library/hh873177(v=vs.110).aspx

那已经不对了.任何方法async都是异步的,然后它说它应该返回一个TaskTask<T>- 这不适合调用堆栈顶部的方法,例如Button_Click,或者async void.

当然,你必须考虑约定的重点是什么?

您可以说Async后缀约定是与API用户通信该方法是等待的.对于要等待的方法,它必须返回Taskvoid,或者Task<T>返回值返回方法,这意味着只有后者可以后缀Async.

或者您可以说Async后缀约定是传达方法可以立即返回,放弃当前线程以执行其他工作并可能导致竞争.

这篇Microsoft doc引用说:

按照惯例,您将"Async"附加到具有Async或async修饰符的方法的名称.

http://msdn.microsoft.com/en-us/library/hh191443.aspx#BKMK_NamingConvention

哪个甚至没有提到你自己的异步方法返回Task需要Async后缀,我认为我们都同意他们这样做.


所以这个问题的答案可能是:两者兼而有之.在这两种情况下,您都需要附加Async带有async关键字的方法并返回TaskTask<T>.


我要请Stephen Toub澄清情况.

更新

所以我做了.这就是我们的好人所写的:

如果公共方法是任务返回并且本质上是异步的(与已知总是同步执行到完成但由于某种原因仍然返回任务的方法相反),它应该具有"异步"后缀.这是指南.这里命名的主要目标是使消费者对功能非常明显,被调用的方法可能无法同步完成所有工作; 它当然也有助于使用同步和异步方法公开功能的情况,这样你需要一个名称差异来区分它们.该方法如何实现其异步实现对于命名无关紧要:是否使用async/await来获取编译器的帮助,或者是否直接使用System.Threading.Tasks中的类型和方法(例如TaskCompletionSource)并不重要,如就方法的使用者而言,这不会影响方法的签名.

当然,指南总是有例外.在命名的情况下最值得注意的是整个类型的存在理由是提供异步聚焦功能的情况,在这种情况下,在每个方法上使用Async都是过度的,例如,任务本身产生其他任务的方法.

对于返回空隙的异步方法,不希望将它们放在公共表面区域中,因为调用者没有很好的方法知道异步工作何时完成.但是,如果必须公开公开返回void的异步方法,那么您可能希望有一个名称来表示正在启动异步工作,如果有意义,可以使用"Async"后缀.鉴于这种情况应该是多么罕见,我认为这确实是一种逐案的决定.

史蒂夫,我希望有所帮助

斯蒂芬的开场判决的简洁指导已经足够明确了.它排除了async void因为想要使用这样的设计创建公共API是不寻常的,因为实现异步void的正确方法是返回一个普通的Task实例并让编译器发挥其神奇作用.但是,如果您确实需要a public async void,则Async建议添加.其他堆栈顶层async void方法(如事件处理程序)通常不公开,并且无关紧要/合格.

对我来说,它告诉我,如果我发现自己想知道后缀Async是什么async void,我可能应该把它变成一个async Task让呼叫者可以等待它,然后追加Async.

  • 好吧很遗憾我们没有方法调用的编译时检查哦等等.如果我将方法**Get**或**GetAsync**命名为并且不在调用端使用**await**,则编译将无法构建.所以这个约定是SILLY并且真的违反许多Microsoft Style指南,比如避免使用`PersonString`或`PriceDecimal`之类的东西,所以为什么要使用`GetAsync`-异步API的API使用者不必担心这个,因为请求总是在所有任务之后返回无论如何完整.它很傻,真烦人.但它只是另一个公约,没有人真正知道为什么它在那里. (14认同)
  • 我觉得这个惯例很愚蠢。有三种自动指示方法是异步的:1.返回类型为Task。2.代码完成提供了一个等待的提示3. IDE将通过绿色下划线并显示编译器警告来警告您。所以我完全同意@ppumkin。异步后缀就像您这样写一个属性一样愚蠢:public Lazy &lt;Customer&gt; CustomerLazy。谁会这样做! (5认同)
  • @ppumkin:默认情况下,未能等待异步方法会导致编译时警告;不是构建错误。 (3认同)
  • @ppumkin:正如Stephen所指出的,本质上可以很容易地实现异步方法而无需使用async / await,因此,调用者除了名称是否具有异步功能外,没有任何其他指示。 (2认同)
  • @Marco 我在 GitHub 上提出了这个想法,认为这是最好的地方,但没有参与,我认为我会得到:https://github.com/dotnet/core/issues/1464 (2认同)

Jon*_*ved 46

我构建了许多API服务和其他应用程序,这些应用程序调用其他大多数代码都运行异步的系统.

我遵循的经验法则是:

如果同时存在返回同一事物的非异步和异步方法,则使用Async后缀异步.否则不是.

例子:

只有一种方法:

public async Task<User> GetUser() { [...] }
Run Code Online (Sandbox Code Playgroud)

具有两个签名的相同方法:

public User GetUser() { [...] }

public async Task<User> GetUserAsync() { [...] }
Run Code Online (Sandbox Code Playgroud)

这是有道理的,因为它返回的是相同的数据,但唯一不同的是返回数据的方式,而不是数据本身.

我还认为这种命名约定是存在的,因为需要引入异步方法并仍然保持向后兼容性.

我认为新代码不应该使用Async后缀.它就像返回类型的String或Int一样显而易见,如前所述.

  • 我同意,特别是通常你需要"一路异步",在这种情况下,后缀是多余的 - 将它附加到90%的代码是什么意义;) (6认同)
  • 此技术的问题在于,如果您稍后创建非异步版本,则无法使用您首选的“GetUser()”名称。 (4认同)
  • 这是最好的解决方案。在不知不觉中,我一直在API中进行相同的操作。 (2认同)
  • 这比后缀“异步”所有异步应用程序方法更好 (2认同)
  • 这是务实的方法。将Async附加到具有async修饰符的每个方法上的方法都是Hungarian Notation2019。@David如果最终要在以后添加一个非异步版本,请重命名这些方法并遵循命名约定,否则就不要这样做。 (2认同)

Ðаn*_*Ðаn 23

使用"Async"后缀方法名称的约定是什么.

基于任务的异步模式(TAP)规定方法应始终返回Task<T>(或Task)并使用Async后缀命名; 这与使用分开async.双方Task<bool> Connect()并会编译和运行得很好,但你不会是TAP命名约定以下.asyncTask<bool> Connect()

方法是否应该包含async修饰符,或者它只是返回Task?

如果方法的主体(无论返回类型或名称)包括await,您必须使用async; 并且编译器会告诉你"'await'运算符只能在异步方法中使用....".返回Task<T>Task不足以避免使用async.有关详细信息,请参阅async(C#参考).

即哪些签名是正确的:

双方并妥善遵循TAP约定.你总是可以使用关键字,但你会得到一个编译器警告"这个异步方法缺少'等待'运算符并且将同步运行...."如果正文不使用.asyncTask<bool> ConnectAsync()Task<bool> ConnectAsync()asyncawait

  • @lejon:你应该改进这个问题; "对战" 代码片段清楚地表明问题(完整地)是关于异步的,因为这是唯一的区别. (3认同)
  • 他指的是您是否将“Async”附加到方法名称,而不是您是否使用 `async` 关键字。 (2认同)
  • @Servy这是一个两部分的问题.第一部分,正如您所说的是是否将"Async"附加到方法名称.第二部分是否使用`async`修饰符.另请参见OP示例,`public async Task <bool> ConnectAsync()`(带有`async`修饰符)vs`public Task <bool> ConnectAsync()`(不带`async`修饰符).在两种情况下,方法*name*本身都具有后缀"Async". (2认同)
  • 这不是一个两部分问题.问题是,应该将"Async"附加到返回`Task`的方法的方法名称或具有`async Task`的方法. (2认同)

Ser*_*rvy 11

或者它只是返回任务?

那.该async关键字是不是在这里真正的问题.如果在不使用async关键字的情况下实现异步,则一般意义上该方法仍然是"异步".


Ton*_*ina 7

由于TaskTask<T>都是等待类型,它们代表一些异步操作.或者至少他们应该代表.

您应该Async为一个方法添加后缀,在某些情况下(不一定是全部),它不返回值,而是返回正在进行的操作的包装器.该包装器通常是一个Task,但在Windows RT上它可以IAsyncInfo.遵循您的直觉并记住,如果您的代码的用户看到该Async功能,他或她将知道该方法的调用与该方法的结果分离,并且他们需要相应地采取行动.

请注意,有方法,如Task.DelayTask.WhenAll其返回Task,但没有Async后缀.

另请注意,有一些async void方法代表fire和forget异步方法,您应该更好地意识到该方法是以这种方式构建的.


Fre*_*red 6

我认为如果该方法返回的是Task,则无论方法是否使用async修饰符声明,都应使用Async后缀。

其背后的原因是在接口中声明了名称。接口声明返回类型为Task。然后,该接口有两种实现,一种实现是使用async修饰符实现的,另一种则不是。

public interface IFoo
{
    Task FooAsync();
}

public class FooA : IFoo
{
    public Task FooAsync() { /* ... */ }
}

public class FooB : IFoo
{
    public async Task FooAsync() { /* ... */ }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是千真万确的。我们总是在任何地方使用接口,并且接口不能声明为异步。所以关于使用 Async 后缀的官方指南对我来说似乎完全是无稽之谈。我认为 async 关键字只是一个实现细节,是方法内部的一部分,不应该影响其名称或任何外部内容。 (2认同)

Dav*_*dRR 5

带有异步和等待的异步编程(C#)中,Microsoft提供以下指导:

命名约定

按照约定,您可以在具有async修饰符的方法名称后附加“ Async” 。

您可以忽略事件,基类或接口协定建议使用其他名称的约定。例如,您不应重命名常见事件处理程序,例如Button1_Click

我发现此指南不完整且不令人满意。这是否意味着在没有async修饰符的情况下,应将此方法命名为Connect而不是ConnectAsync

public Task<bool> ConnectAsync()
{
    return ConnectAsyncInternal();
}
Run Code Online (Sandbox Code Playgroud)

我不这么认为。正如指出的回答言简意赅@Servy和更详细的解答@Luke Puplett,我认为这是适当的,确实预计这一方法应该被命名为ConnectAsync(因为它返回一个awaitable)。为了进一步支持这一点,@ John Skeet在另一个问题的答案中Async无论async修饰符是否存在,都将附加到方法名称之后。

最后,在另一个问题,认为此评论@Damien_The_Unbeliever

async/await是方法的实现细节。就调用者而言,是否声明您的方法async Task Method()还是just 无关紧要。(实际上,您可以在稍后的时间自由在这两者之间进行更改,而不会被认为是重大更改。)Task Method()

由此推断,正是方法的异步性质决定了应如何命名。方法的用户甚至不知道async在其实现中是否使用了修饰符(没有C#源代码或CIL)。