2017-09-14 24 views
0

我有一组从基类继承的命令。基类具有以下声明:异步任务方法,不需要等待

public virtual async Task Execute(object parameter){} 

继承类提供了对此方法的重写,并不是所有这些都等待任务。在这些情况下,编译器会生成一个警告:

这个异步方法缺少'await'操作符并且将同步运行。 考虑使用'await'操作符来等待非阻塞API调用 或'await Task.Run(...)'在后台线程上执行CPU绑定工作。

显式提供任务完整返回值是否正确?

public override async Task Execute(object parameter) { 
    //code containing no await statements... 
    await Task.CompletedTask; 
} 

回答

5

你应该避免使用覆盖的方法,其不await任何事情里面async关键字,而是只返回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

+2

我显示差异我的原始代码的输出和你从dotPeek推荐的更改的输出,它们是相同的(调试dll)。你的例子也删除了编译器的警告,这是我原来的动机。 - 谢谢! – jchristof

3

作为对比,异常处理方式的差异。

如果非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... 
}