【问题标题】:Compiler warning CS4014 not emitted during build编译期间未发出编译器警告 CS4014
【发布时间】:2017-02-13 17:04:12
【问题描述】:

当调用的方法位于引用的程序集中时,编译器警告 CS4014(调用异步方法而不等待结果)在构建期间不会作为警告发出。

当被调用的方法在 same 程序集中时,会​​正确发出警告。

当两个项目包含在同一个解决方案中时,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() 下显示一条红色波浪线。

【问题讨论】:

  • 如何编译程序集?你能展示你正在使用的语句吗?
  • 我正在使用 Visual Studio 编译程序集 -> 构建解决方案。这两个项目在编译时都带有 (/warn:4) 上的所有警告。如果有帮助,我可以将参数粘贴到 csc.exe 但这些参数很长。
  • 好吧,我以为你在手动运行构建。

标签: c# visual-studio roslyn


【解决方案1】:

async 修饰符允许您编写更方便地返回 Task 的代码(使用 await),但它在 IL* 中没有表示形式。在已编译的程序集中,该方法在编译器看来就像public static Task&lt;string&gt; DoSomething,并且在不等待其结果的情况下调用它们不会触发警告(即使这些方法位于同一个程序集中)。将Class1.DoSomething 替换为返回任务且应该等待的其他内容(例如Task.Delay(2000)),您同样会看到编译器不会发出警告。但是,当所有源代码都可用时,编译器(我指的是 Roslyn 编译器)可以将方法识别为 async,因为修饰符仍然是语法树的一部分。

那么为什么当你调用一个返回Task的方法而不使用结果时编译器不总是发出警告,不管它是否碰巧是用async编写的?好问题。虽然有很多合法的场景,您不等待 Task(例如,因为您想将其传递给 Task.WhenAll)所有这些都涉及将 Task 存储在其他地方,这不会提高警告。调用返回 Task 的方法并完全丢弃结果几乎可以肯定是一个错误(如果是故意的,则有 elegant ways 抑制警告),这就是为什么首先存在此警告的原因。

我怀疑这个警告的实现可以使用调整(或用新警告替换),但只有从事编译器工作的人才能确定。


*:这不是真的; async 方法有一个 AsyncStateMachineAttribute 应用于它们以便更方便的调试。但是,无论出于何种原因,编译器都不会使用它来识别程序集中的async 方法。可以说,它也不应该:就方法返回的Task 而言,async 没有什么特别之处。但是,如果他们想准确地保留 CS4104 的规定语义(如果 async 方法的结果未使用,则发出警告),这将是一种方法。

【讨论】:

  • 也许Task.Run() 用于启动即发即弃任务的流行意味着误报率太高而无法无条件发出警告。
  • 很好的答案,谢谢!我一直在使用一个名为 MassTransit 的库,它使用一种模式,它在内部存储一个任务并返回相同的任务。调用者有两种选择:1. 等待返回的任务以确保操作正确完成或 2. 让 MassTransit 等待任务并处理任何异常。因此,无需等待即可合法地使用调用方法。在 Roslyn GitHub 存储库上提出问题会有所帮助吗?
  • @BenjaminWegman:提供两种方法可能会更好地服务于这种使用模式,一种返回任务,另一种不返回。但是,如果您坚持使用该界面,那么像链接答案中那样故意丢弃任务的.Forget() 扩展方法可能是值得的。至于在 Roslyn 上提出问题,我根本没有参与该项目,所以我不了解错误文化。从这个意义上说,我没有资格发表任何“有帮助”的声明。
猜你喜欢
  • 2019-11-23
  • 2011-05-09
  • 1970-01-01
  • 2013-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多