构建期间未发出编译器警告 CS4014
Compiler warning CS4014 not emitted during build
当调用的方法在引用的程序集中时,编译器警告 CS4014(调用异步方法而不等待结果)不会在生成期间作为警告发出。
当调用的方法在 相同的 程序集中时,正确发出警告。
当两个项目包含在同一解决方案中时,编译器警告 是 在 Visual Studio 中发出信号。
差异似乎是由于编译器只有编译引用的程序集而 Visual Studio 有两个程序集的源代码造成的。
问题是:为什么会有这两种不同的行为?有什么办法可以在编译期间发出 CS4014 警告?
要复制此行为,请设置两个 class 库,它们都有一个代码文件:
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
修饰符可以让你写出returns一个Task
更方便的代码(用await
),但是它在IL[=38中没有表示=]*。在已编译的程序集中,该方法对编译器来说只是看起来像 public static Task<string> DoSomething
,并且在不等待其结果的情况下调用它们不会触发警告(即使这些方法位于同一程序集中)。将 Class1.DoSomething
替换为 returns 任务并应该等待的其他内容(例如 Task.Delay(2000)
),您同样会看到编译器不会发出警告。但是,当所有源代码可用时,编译器(编译器我指的是 Roslyn)可以将该方法识别为 async
,因为修饰符仍然是语法树的一部分。
那么,当您调用返回 Task
的方法而不使用结果时,无论它是否碰巧是使用 async
编写的,为什么编译器不总是发出警告?好问题。虽然有很多合法的场景,你不会等待 Task
(因为你想将它传递给 Task.WhenAll
),但所有这些都涉及将 Task
存储在其他地方,这不会引发警告。调用一个 returns 一个 Task
的方法并完全丢弃结果几乎肯定是一个错误(如果是故意的,会有 elegant ways 抑制警告),这就是这个警告存在的原因首先。
我怀疑这个警告的实现可以使用调整(或用新警告替换),但只有从事编译器工作的人才能确定。
*:事实并非如此; async
方法应用了 AsyncStateMachineAttribute
以便于调试。但是,无论出于何种原因,编译器都不会使用它来跨程序集识别 async
方法。也不应该,可以说:就 Task
方法 returns 而言,async
并没有什么特别之处。但是,如果他们想完全保留 CS4104 的规定语义(如果未使用 async
方法的结果则发出警告)这将是一种方法。
当调用的方法在引用的程序集中时,编译器警告 CS4014(调用异步方法而不等待结果)不会在生成期间作为警告发出。
当调用的方法在 相同的 程序集中时,正确发出警告。
当两个项目包含在同一解决方案中时,编译器警告 是 在 Visual Studio 中发出信号。
差异似乎是由于编译器只有编译引用的程序集而 Visual Studio 有两个程序集的源代码造成的。
问题是:为什么会有这两种不同的行为?有什么办法可以在编译期间发出 CS4014 警告?
要复制此行为,请设置两个 class 库,它们都有一个代码文件:
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
修饰符可以让你写出returns一个Task
更方便的代码(用await
),但是它在IL[=38中没有表示=]*。在已编译的程序集中,该方法对编译器来说只是看起来像 public static Task<string> DoSomething
,并且在不等待其结果的情况下调用它们不会触发警告(即使这些方法位于同一程序集中)。将 Class1.DoSomething
替换为 returns 任务并应该等待的其他内容(例如 Task.Delay(2000)
),您同样会看到编译器不会发出警告。但是,当所有源代码可用时,编译器(编译器我指的是 Roslyn)可以将该方法识别为 async
,因为修饰符仍然是语法树的一部分。
那么,当您调用返回 Task
的方法而不使用结果时,无论它是否碰巧是使用 async
编写的,为什么编译器不总是发出警告?好问题。虽然有很多合法的场景,你不会等待 Task
(因为你想将它传递给 Task.WhenAll
),但所有这些都涉及将 Task
存储在其他地方,这不会引发警告。调用一个 returns 一个 Task
的方法并完全丢弃结果几乎肯定是一个错误(如果是故意的,会有 elegant ways 抑制警告),这就是这个警告存在的原因首先。
我怀疑这个警告的实现可以使用调整(或用新警告替换),但只有从事编译器工作的人才能确定。
*:事实并非如此; async
方法应用了 AsyncStateMachineAttribute
以便于调试。但是,无论出于何种原因,编译器都不会使用它来跨程序集识别 async
方法。也不应该,可以说:就 Task
方法 returns 而言,async
并没有什么特别之处。但是,如果他们想完全保留 CS4104 的规定语义(如果未使用 async
方法的结果则发出警告)这将是一种方法。