I've run into this issue not only when migrating legacy projects to use async/await in C# .NET, but even just day to day on greenfields projects. The issue I'm talking about involves code that looks like so :

static async Task Main(string[] args) {     MyAsyncMethod(); // Oops I forgot to await this! }  static async Task MyAsyncMethod() {     await Task.Yield(); }            

It can actually be much harder to diagnose than you may think. Due to the way async/await works in C#, your async method may not *always* be awaited. If the async method completes before it has a chance to wait, then your code will actually work much the same as you expect. I have had this happen often in development scenarios, only for things to break only in test. And the excuse of "but it worked on my machine" just doesn't cut it anymore!

In recent versions of .NET and Visual Studio, there is now a warning that will show to tell you your async method is not awaited. It gives off the trademark green squiggle :

And you'll receive a build warning with the text :

CS4014 Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

The problem with this is that the warning isn't always immediately noticeable. On top of this, a junior developer may not take heed of the warning anyway.

What I prefer to do is add a line to my csproj that looks like so :

<PropertyGroup>     <WarningsAsErrors>CS4014;</WarningsAsErrors> </PropertyGroup>            

This means that every async method that is not awaited will actually stop the build entirely.

Disabling Errors By Line

But what if it's one of those rare times you actually do want to fire and forget (Typically for desktop or console applications), but now you've just set up everything to blow up? Worse still the error will show if you are inside an async method calling a method that returns a Task, even if the called method is not itself async.

But we can disable this on a line by line basis like so :

static async Task Main(string[] args) {     #pragma warning disable CS4014      MyAsyncMethod(); // I don't want to wait this for whatever reason, it's not even async!     #pragma warning restore CS4014 }  static Task MyAsyncMethod() {     return Task.CompletedTask; }            

Non-Awaited Tasks With Results

Finally, the one thing I have not found a way around is like so :

static async Task Main(string[] args) {     var result = MyAsyncMethodWithResult();     var newResult = result + 10;//Error because result is actually an integer.  }  static async Task<int> MyAsyncMethodWithResult() {     await Task.Yield();     return 0; }            

This code will actually blow up. The reason being that we expect the value of result to be an integer, but in this case because we did not await the method, it's a task. But what if we pass the result to a method that doesn't care about the type like so :

static async Task Main(string[] args) {     var result = MyAsyncMethodWithResult();     DoSomethingWithAnObject(result); }  static async Task MyAsyncMethodWithResult() {     await Task.Yield();     return 0; }  static void DoSomethingWithAnObject(object myObj) { }            

This will not cause any compiler warnings or errors (But it will cause runtime errors depending on what DoSomethingWithAnObject does with the value).

Essentially, I found that the warning/error for non awaited tasks is not shown if you assign the value to a variable. This is even the case with Tasks that don't return a result like so :

static async Task Main(string[] args) {     var result = MyAsyncMethod(); // No error }  static async Task MyAsyncMethod() {     await Task.Yield(); }            

I have searched high and low for a solution for this but most of the time it leads me to stack overflow answers that go along the lines of "Well, if you assigned the value you MIGHT actually want the Task as a fire and forget". Which I agree with, but 9 times out of 10, is not going to be the case.

That being said, turning the compiler warnings to errors will catch most of the errors in your code, and the type check system should catch 99% of the rest. For everything else… "Well it worked on my machine".