我有一组从基类继承的命令。基类具有以下声明:
public virtual async Task Execute(object parameter){}
继承类提供了此方法的覆盖,并非所有类都等待任务。在这些情况下,编译器会生成警告:
这种异步方法缺少'await'运算符并将同步运行。 考虑使用'await'运算符来等待非阻塞API调用, 或'等待Task.Run(...)'在后台线程上进行CPU绑定工作。
明确提供任务完整返回值是否正确?
public override async Task Execute(object parameter) {
//code containing no await statements...
await Task.CompletedTask;
}
答案 0 :(得分:7)
您应该避免在重写方法中使用async
关键字,而不是await
任何内容,而只是返回Task.CompletedTask
:
public override Task Execute(object parameter) {
//code containing no await statements...
return Task.CompletedTask; // or Task.FromResult(0) for older .NET versions
}
这是(少数)用于此类"模拟"的用例之一。您可以通过looking at this question了解Task.FromResult
的任务。这些注意事项仍然适用于Task.CompletedTask
。
答案 1 :(得分:4)
作为对应点, 处理异常的方式不同。
如果从非async
版本的同步代码中引发异常,那么该异常将直接引发给调用者(对于具有异步签名的方法来说非常罕见)。
如果async
版本中的同步代码引发异常,则捕获该异常并将其放在返回的任务上(这是具有异步签名的方法的预期行为)。
因此,如果这样调用该方法,则不同的实现将具有不同的异常语义:
var task = Execute(parameter); // non-async exceptions raised here
...
await task; // async exceptions raised here
但是,大多数情况下,该方法被调用并立即等待,因此这两个语义合并在一起:
await Execute(parameter); // both async and non-async exceptions raised here
可能无关紧要,但我相信这是一个值得注意的重要区别。
如果异常语义对您很重要,那么您做想要使用async
围绕同步代码生成状态机,并且可以在代码中禁用警告:
#pragma warning disable 1998 // use async keyword to capture exceptions
public override async Task Execute(object parameter) {
#pragma warning restore 1998
//code containing no await statements...
}