Compiler warning CS4014 (calling async method without awaiting result) is not emitted as a warning during build when the called method is in a referenced assembly.
When the called method is in the same assembly the warning is correctly emitted.
The compiler warning is signaled in Visual Studio when both projects are contained in the same solution.
The difference seems to be caused by the compiler having only the compiled referenced assembly and Visual Studio having the source code to both assemblies.
The question is: why have these two different behaviors? And is there any way to have the CS4014 warning emitted during compilation?
To replicate this behavior setup two class libraries, both having one code file:
TestClassLibrary1
public class Class1
{
public static async Task<string> DoSomething()
{
return await Task.FromResult("test");
}
}
TestClassLibrary2 (referencing TestClassLibrary1)
public class Class2
{
public void CallingDoSomething()
{
Class1.DoSomething();
}
}
Compiling these projects will complete without warnings. Opening them in the same solution in Visual Studio will result in 1 error being shown in the Error List and a red squiggly line under Class1.DoSomething()
.
The async
modifier allows you to write code that returns a Task
more conveniently (with await
), but it has no representation in IL*. In a compiled assembly, the method simply looks like public static Task<string> DoSomething
to the compiler, and calling those without awaiting their result doesn't trigger the warning (even if the methods live in the same assembly). Replace Class1.DoSomething
with something else that returns a task and ought to be awaited (say Task.Delay(2000)
) and you'll likewise see the compiler doesn't warn. When all of the source code is available, however, the compiler (and by compiler I mean Roslyn) can identify the method as async
because the modifier is still part of the syntax tree.
So why doesn't the compiler just always warn when you call a method returning a Task
without using the result, regardless of whether it happened to be written using async
? Good question. While there are plenty of legitimate scenarios where you don't wait to await a Task
(because you want to pass it to Task.WhenAll
for example) all of these involve storing the Task
somewhere else, which would not raise the warning. Calling a method that returns a Task
and discarding the result entirely is almost certainly a mistake (and when it's intentional, there are elegant ways of suppressing the warning), which is why this warning exists in the first place.
I suspect the implementation of this warning could use a tweak (or a replacement with a new warning), but only the people working on the compiler would know that for sure.
*: this isn't actually true; async
methods have an AsyncStateMachineAttribute
applied to them for more convenient debugging. But, for whatever reason, the compiler doesn't use this to identify async
methods across assemblies. Nor should it, arguably: there's nothing particularly special about async
in terms of the Task
the method returns. But if they wanted to preserve the stated semantics of CS4104 exactly (warn if the result of an async
method is unused) this would be one way to do it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With