c#-编译期间未发出编译器警告CS4014

codeday· 2019-10-26
本文来自 codeday ,作者 codeday
当被调用方法在引用程序集中时,编译器警告CS4014(在不等待结果的情况下调用异步方法)不会作为警告发出.

当被调用的方法在同一程序集中时,将正确发出警告.

当两个项目包含在同一解决方案中时,在Visual Studio中会发出编译器警告.

造成这种差异的原因似乎是编译器仅具有编译的引用程序集,而Visual Studio具有两个程序集的源代码.

问题是:为什么这两种行为不同?并且有什么办法可以在编译期间发出CS4014警告?

要复制此行为,请设置两个类库,每个类库都有一个代码文件:

TestClassLibrary1

public class Class1
{
    public static async Task<string> DoSomething()
    {
        return await Task.FromResult("test");
    }
}

TestClassLibrary2(引用TestClassLibrary1)

public class Class2
{
    public void CallingDoSomething()
    {
        Class1.DoSomething();
    }
}

这些项目的编译将完成,而不会发出警告.在Visual Studio中的相同解决方案中打开它们将导致在错误列表中显示1条错误,并在Class1.DoSomething()下显示一条红色的波浪线.

最佳答案
使用async修饰符,您可以编写代码以更方便地(带有await)返回Task,但是在IL *中没有表示形式.在编译的程序集中,该方法看起来像是公共静态Task< string>.对编译器执行DoSomething并在不等待其结果的情况下调用它们不会触发警告(即使这些方法位于同一程序集中).将Class1.DoSomething替换为可以返回任务并应等待的其他内容(例如Task.Delay(2000)),您同样会看到编译器未发出警告.但是,当所有源代码均可用时,编译器(和编译器,我的意思是Roslyn)可以将方法标识为异步,因为修饰符仍是语法树的一部分.

那么,为什么在调用返回Task的方法而不使用结果时,编译器不总是发出警告,无论它是否恰好是使用异步编写的?好问题.虽然有很多合法的情况下您不必等待任务(例如,因为要将其传递给Task.WhenAll),但所有这些都涉及将Task存储在其他位置,这不会发出警告.几乎肯定可以肯定的是,调用一个返回Task的方法并完全放弃结果是一个错误(故意这样做时,有elegant ways个抑制警告),这就是为什么该警告首先存在的原因.

我怀疑此警告的实现可能会进行调整(或用新警告替代),但只有从事编译器工作的人员才能确定这一点.

*:实际上并非如此;异步方法已将AsyncStateMachineAttribute应用于它们,以便更方便地进行调试.但是,无论出于何种原因,编译器都不会使用它来识别跨程序集的异步方法.可以争辩的也不是:就方法返回的Task而言,异步没有什么特别的地方.但是,如果他们想精确地保留CS4104的规定语义(如果未使用异步方法的结果则发出警告),这将是一种实现方法.