使用“Async”后缀方法名称的约定是什么?
“Async”后缀是否应仅附加到使用async
修饰符声明的方法?
public async Task<bool> ConnectAsync()
或者这个方法只返回Task<T>
或Task
就够了吗?
public Task<bool> ConnectAsync()
我认为即使从Microsoft文档中,事实也是模棱两可的:
在Visual Studio 2012和.NET Framework 4.5中,任何使用
async
关键字(在Visual Basic中为Async
)的方法都被视为异步方法,C#和Visual Basic编译器执行必要的转换以通过使用异步方式实现该方法TAP。异步方法应返回Task
或Task<TResult>
对象。
http://msdn.microsoft.com/en-us/library/hh873177(v=vs.110).aspx
那已经不对了。使用async
的任何方法都是异步的,然后它说它应该返回Task
或Task<T>
- 这不适用于调用堆栈顶部的方法,例如Button_Click或async void
。
当然,你必须考虑约定的重点是什么?
你可以说Async
后缀约定是向API用户传达该方法是可以等待的。对于要等待的方法,它必须返回Task
表示void,或者Task<T>
表示返回值的方法,这意味着只有后者可以用Async
作为后缀。
或者你可能会说Async
后缀约定是传达方法可以立即返回,放弃当前线程执行其他工作并可能导致比赛。
这篇Microsoft doc引用说:
按照惯例,您将“Async”附加到具有Async或async修饰符的方法的名称。
http://msdn.microsoft.com/en-us/library/hh191443.aspx#BKMK_NamingConvention
哪个甚至没有提到你自己的返回Task
的异步方法需要Async
后缀,我认为我们都同意他们这样做。
所以这个问题的答案可能是:两者兼而有之。在这两种情况下,您需要将Async
附加到使用async
关键字并返回Task
或Task<T>
的方法。
我将要求Stephen Toub澄清情况。
更新
所以我做了。这就是我们的好人所写的:
如果公共方法是任务返回并且本质上是异步的(与已知总是同步执行到完成但由于某种原因仍然返回任务的方法相反),它应该具有“异步”后缀。这是指南。这里命名的主要目标是使消费者对功能非常明显,被调用的方法可能无法同步完成所有工作;它当然也有助于使用同步和异步方法公开功能的情况,这样你需要一个名称差异来区分它们。该方法如何实现其异步实现对命名无关紧要:是否使用async / await来获取编译器的帮助,或者是否直接使用System.Threading.Tasks中的类型和方法(例如TaskCompletionSource)并不重要,如就方法的使用者而言,这不会影响方法的签名。
当然,指南总是有例外。在命名的情况下最值得注意的一种情况是整个类型的存在理由是提供异步聚焦功能,在这种情况下,在每种方法上使用Async都会过度,例如:任务本身产生其他任务的方法。
对于返回空隙的异步方法,不希望将它们放在公共表面区域中,因为调用者没有很好的方法知道异步工作何时完成。但是,如果必须公开公开返回void的异步方法,那么您可能希望有一个名称来表示正在启动异步工作,如果有意义,可以使用“Async”后缀。鉴于这种情况应该是多么罕见,我认为这确实是一种逐案的决定。
史蒂夫,我希望有所帮助
斯蒂芬的开场判决的简洁指导已经足够明确了。它排除了async void
,因为想要创建具有这种设计的公共API是不寻常的,因为实现异步void的正确方法是返回一个普通的Task
实例并让编译器发挥其神奇作用。但是,如果你确实想要一个public async void
,那么建议添加Async
。其他顶级的async void
方法(如事件处理程序)通常不公开,并且无关紧要/合格。
对我来说,它告诉我,如果我发现自己想知道在Async
上加上async void
的后缀,我可能应该把它变成一个async Task
,以便呼叫者可以等待它,然后追加Async
。
我构建了许多API服务和其他应用程序,这些应用程序调用其他系统,其中我的大多数代码都是异步运行的
我遵循的经验法则是:
如果同时存在返回同一事物的非异步和异步方法,则使用Async后缀异步。否则不是。
例子:
只有一种方法:
public async Task<User> GetUser() { [...] }
具有两个签名的相同方法:
public User GetUser() { [...] }
public async Task<User> GetUserAsync() { [...] }
这是有道理的,因为它返回的是相同的数据,但唯一不同的是返回数据的方式,而不是数据本身。
我还认为这种命名约定是存在的,因为需要引入异步方法并仍然保持向后兼容性。
我认为新代码不应该使用Async后缀。它就像返回类型的String或Int一样显而易见,如前所述。
使用“Async”后缀方法名称的约定是什么。
Task-based Asynchronous Pattern (TAP)规定方法应始终返回Task<T>
(或Task
)并以Async后缀命名;这与async
的使用是分开的。 Task<bool> Connect()
和async
Task<bool> Connect()
都可以编译并运行得很好,但是你不会遵循TAP命名约定。
该方法是否应该包含
async
修饰符,还是足以让它只返回Task?
如果方法的主体(无论返回类型或名称)包括await
,则必须使用async
;并且编译器会告诉你“'await'运算符只能在异步方法中使用....”。返回Task<T>
或Task
并不足以避免使用async
。有关详细信息,请参阅async (C# Reference)。
即哪些签名是正确的:
async
Task<bool> ConnectAsync()
和Task<bool> ConnectAsync()
都遵循TAP惯例。你总是可以使用async
关键字,但你会得到一个编译器警告“这个异步方法缺少'等待'运算符并将同步运行....”如果正文不使用await
。
或者它只是返回任务?
那。 async
关键字不是真正的问题。如果在不使用async
关键字的情况下实现异步,则一般意义上该方法仍然是“异步”。
由于Task
和Task<T>
都是等待类型,它们代表了一些异步操作。或者至少他们应该代表。
您应该将后缀Async
添加到一个方法中,该方法在某些情况下(不一定是全部)不返回值,而是返回正在进行的操作的包装器。这个包装器通常是Task
,但在Windows RT上它可以是IAsyncInfo
。遵循您的直觉并记住,如果您的代码的用户看到Async
函数,他或她将知道该方法的调用与该方法的结果分离,并且他们需要相应地采取行动。
请注意,有一些方法,如Task.Delay
和Task.WhenAll
返回Task
,但没有Async
后缀。
另请注意,有async void
方法表示fire和forget异步方法,您应该更好地意识到该方法是以这种方式构建的。
我认为如果它返回一个Task,它应该使用Async-suffix,无论该方法是否使用async
修饰符声明。
其背后的原因是该名称是在界面中声明的。接口声明返回类型,即Task
。然后有两个接口的实现,一个实现使用async
修饰符实现它,另一个实现不实现它。
public interface IFoo
{
Task FooAsync();
}
public class FooA : IFoo
{
public Task FooAsync() { /* ... */ }
}
public class FooB : IFoo
{
public async Task FooAsync() { /* ... */ }
}
在Asynchronous Programming with async and await (C#)中,Microsoft提供以下指导:
命名惯例
按照惯例,您将“Async”附加到具有异步修饰符的方法的名称。
您可以忽略事件,基类或接口契约建议不同名称的约定。例如,您不应重命名常见事件处理程序,例如
Button1_Click
。
我发现这个指导不完整且不满意。这是否意味着在没有async
修饰符的情况下,此方法应该命名为Connect
而不是ConnectAsync
?
public Task<bool> ConnectAsync()
{
return ConnectAsyncInternal();
}
我不这么认为。正如concise answer的@Servy和detailed answer的@Luke Puplett所表明的那样,我认为这种方法应该被命名为ConnectAsync
(因为它返回一个等待的)是合适的,并且确实是预期的。为了进一步支持这一点,@John Skeet在this answer中的另一个问题将Async
附加到方法名称,无论async
修饰符的存在。
最后,在another question上,考虑this comment的@Damien_The_Unbeliever:
async/await
是您的方法的实现细节。关于你的方法是否被宣布为async Task Method()
或只是Task Method()
,至关重要的是你的呼叫者。 (事实上,你可以在以后的某个时间点自由地改变它们,而不会被认为是一个突破性的变化。)
从那里,我推断出该方法的异步性质决定了它应该如何命名。该方法的用户甚至不知道在其实现中是否使用了async
修饰符(没有C#源代码或CIL)。