抑制空异步方法的警告提示

36

让我们假设我有以下函数:

public class Test
{
    public async Task Finalize()
    {
        // We don't need this in this class, so empty body
    }

    /*
     * Additional methods snipped
     */
}

虽然这样做没有问题,但我会收到编译器警告:

此异步方法缺少“await”运算符,将同步运行。考虑使用“await”运算符等待非阻塞API调用,或使用“await Task.Run(...)”在后台线程上执行CPU绑定工作。

如何在不太修改方法的情况下避免此警告?在这种情况下,我无法抛出异常,因为该方法将被调用,但此时绝对不会发生任何事情,因为我所说的类没有任何要结束的内容。


asyncpublic不能与interfaces一起使用。 - Sriram Sakthivel
5个回答

76

这种方法可以避免编译器的警告而不是将其静音:

对于任何有兴趣的人,如果您需要规避这样的编译器警告:

public async Task DoStuff
{
    // This method should stay empty
    // Following statement will prevent a compiler warning:
    await Task.FromResult(0);
}

2
实际答案在这里。编译指示是解决问题的最后一招,长期维护困难。此外,请注释代码并解释您正在做什么。 - csaam
是的,我的答案将防止警告本身,而Eren的答案只会“静音”警告。所以我的答案实际上更正确,但我讨厌标记自己的答案:P - Eisenhorn
没有冒犯Eren的意思,但是我记得写C/C++时需要所有编译器指令才能真正使一个大项目运行。这可能会很混乱。 - csaam
5
不太确定为什么你会说编译指示是最后的努力,它们确实能够完成它们被创建的功能。出现警告时,你应该注意这个警告并告诉编译器:“我知道这是需要小心谨慎的,但我确实需要这个”。我个人认为,为了消除一个警告而等待一个虚假任务是一种hack,而使用编译指示则是按照其预期的方式进行操作。 - Robba
3
没必要创建一个任务并等待该任务,以便您可以创建一个等待该任务的任务。只需从上面的代码中删除 asyncawait,它就可以匹配相同的签名,并且不会生成一堆无用的代码,这些代码不会被优化掉。 - Jon Hanna
14
这篇文章是在.NET 4.6发布之前编写的,现在.NET 4.6提供了Task.CompletedTask。 - ZunTzu

45

在.Net 4.6之前,我们不得不返回一个我们不需要的虚拟值。然而,现在我们可以像这样做:

public async Task MyFunctionAsync()
{
    // Some work here...
    await Task.CompletedTask;
}

或者更好的方法是,从这里的代码中删除asyncawait关键字,因为async不是接口合同的一部分:

public Task MyFunctionAsync()
{
    // Some work here...
    Task.CompletedTask;
}

5
这个回答比所有的都要好。 - Anoop H.N
1
我同意,这很遗憾,因为它太新了,很可能被忽视。 - Jessica Pennell
1
这是最简单的方法,用于异步函数且没有 await 运算符。 - Izzy Helianthus
3
这个答案不完全正确。异步修饰符应该被删除,函数应该返回Task.CompletedTask。 - David
1
@keuleJ 在我看来,这个问题的整个重点在于保持async关键字的同时抑制警告。 - Theodor Zoulias
显示剩余5条评论

36

当您在异步接口中使用同步(或无操作)实现时,这是一个比较常见的问题。

您可以通过返回已完成的Task来实现不带async关键字的Task返回方法,例如:

public Task FinalizeAsync()
{
  return Task.FromResult(0);
}

然而,每次调用仍会分配一个 Task。如果您发现自己经常这样做,可能需要缓存已完成的 Task 实例。我的 AsyncEx 库为此提供了许多 任务常量

public Task FinalizeAsync()
{
  return TaskConstants.Completed;
}

最后,您可能希望查看我在异步处理资源释放方面的博客文章,了解一些替代方法。


4
这里的最佳答案是到目前为止最好的,但是如果有人补充评论指出async不是方法实际签名的一部分,因此调用代码和匹配接口不会受影响,那就更完美了 ;)。 - Jon Hanna
5
此帖子是在.NET 4.6之前编写的,现在.NET 4.6提供了Task.CompletedTask。 - ZunTzu
@JonHanna 啊,谢谢你的澄清!当我声明一个空的虚方法并希望派生类能够异步实现它时,我遇到了这个问题,并没有意识到我可以将虚方法设置为非“async”。 - Tobias J
@Toby 一个方法是通过async/await方式创建Task还是使用另一个创建Task的方法,这取决于实现。从外部来看没有区别,这也是所有接口所能坚持的。 - Jon Hanna
如果您正在实现一个必须重写抽象方法的测试模拟,类似于这样:protected override async Task<AuthenticateResult> HandleAuthenticateAsync(),该怎么办? - Peter Wone
@PeterWone:相同的解决方案适用。测试模拟通常使用Task.FromResult - Stephen Cleary

18

您可以将以下指令放在文件上:

#pragma warning disable 1998

然而,我建议保留警告并遵循其建议。它是有充分理由的警告 ;)

编辑:如果您想仅针对一个方法禁用警告,可以执行以下操作:

#pragma warning disable 1998
async Task Foo() {}
#pragma warning restore 1998

这样做不会禁用与此问题相关的所有编译器警告,而不仅仅是来自一个函数的警告吗? - Eisenhorn
没错,你可以使用 #pragma warning restore... 让我来编辑。 - Eren Ersönmez
这种方法似乎可以很好地工作,而不需要添加实际的主体。我会将两个答案都标记为正确,但你会得到荣誉。谢谢! - Eisenhorn
1
警告是你做了一些傻事的标志,因此得名。只有在你百分之百确定自己没有做傻事时,才应该禁用警告。由于问题中的代码很傻,禁用警告使情况变得更糟,因为至少当你编译它时,它是一个警告你它是愚蠢代码的愚蠢代码,现在它是一个不这样做的愚蠢代码。 - Jon Hanna
@Jon,我已经在答案中提醒过了,但我不确定这是否是个好主意——他们似乎忽略了它 ;) - Eren Ersönmez

-2

移除 "async" 关键字,警告即可消失:

public class Test
{
    public void Finalize()
    {
        // We don't need this in this class, so empty body
    }
 }

2
您可以摆脱 async,只需返回一个 Task 即可。但更改返回类型会将该方法变为外部调用者的限制,从而限制了稍后引入 async 的选项。 - Yishai

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接