Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

No warning when I forget `await` on an interface method call

Tags:

Consider:

using System.Threading.Tasks;  class Program {     static void Main(string[] args)     {         C c = new C();         c.FooAsync(); // warning 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.         ((I)c).FooAsync(); // No warning     } }  class C : I {     public async Task FooAsync()     {     } }  interface I {     Task FooAsync(); } 

If I call the async method directly on the c object, I get a compiler warning. There's potentially a bug here, so I'm glad for the warning.

However, if I make the same call on an interface method, I get no warning. It would be easy to let a bug slip past in this code.

How can I ensure that I don't make this mistake? Is there a pattern I can apply to protect myself?

like image 422
Jay Bazuzi Avatar asked Jul 25 '13 19:07

Jay Bazuzi


People also ask

What happens when you call async method without await?

The call to the async method starts an asynchronous task. However, because no Await operator is applied, the program continues without waiting for the task to complete. In most cases, that behavior isn't expected.

What happens when you call await?

await releases the current thread, but NOT to the thread pool. The UI thread doesn't come from the thread pool. If you run asynchronous method, e.g. ExecuteScalarAsync without async, await keywords, then this method will run asynchronously no matter what. The calling thread won't be affected .

When an asynchronous method is executed the code runs but nothing happens other than compiler warning?

When a asynchronous method is executed, the code runs but nothing happens other than a compiler warning. What is most likely causing the method to not return anything? (A) The return yield statement is missing at the end of the method.

Does await free the thread?

The await keyword, by contrast, is non-blocking, which means the current thread is free to do other things during the wait.


2 Answers

Main is not async, so it can't use await. This seems to confuse the compiler messages slightly. If you put the calls into an actual async method;

static void Main(string[] args) {     Task.Run(async () =>                        {                            C c = new C();                            c.FooAsync();                            ((I) c).FooAsync();                        }); } 

...both will warn.

Line 10: 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.
Line 11: 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.

EDIT: It seems like all methods that return Task inside async methods will warn unless you await or assign them; note that we're working with the interface that doesn't even mention async;

interface I {     Task FooAsync(); }  static void Main(string[] args) {     I i = null;      i.FooAsync();             // Does not warn     // await i.FooAsync();    // Can't await in a non async method     var t1 = i.FooAsync();    // Does not warn      Task.Run(async () =>     {        i.FooAsync();          // Warns CS4014        await i.FooAsync();    // Does not warn        var t2 = i.FooAsync(); // Does not warn     }); } 
like image 95
Joachim Isaksson Avatar answered Sep 20 '22 15:09

Joachim Isaksson


The logic for this warning seems to be:

  • in an async method, warn whenever a Task-returning method is called, but the result is ignored
  • in a normal (non-async) method, warn whenever a Task-returning async method is called, but the result is ignored

For example, look at this (nonsensical) code:

Task NonAsyncMethod() {     AsyncMethod(); // warnig     NonAsyncMethod(); // no warning      return null; // to make the code compile }  async Task AsyncMethod() {     AsyncMethod(); // warning     NonAsyncMethod(); // warning } 

This is why you're not getting the warning with the interface: the interface method isn't (and can't) be marked as async.

I think the reason for this is that in old, pre-async code, it's common to for example call task.ContinueWith() and ignore its result. If the warning was reported in this case too, relatively large amount of old correct code would suddenly become warnings.

A warning should be output when there is a large likelihood of a bug. I think that the cases that are reported are much more likely to be bugs than the case which isn't. So to me, this behavior make sense.

If you want to make sure you don't make this mistake, be careful about calling Task-returning methods from non-async code.

like image 34
svick Avatar answered Sep 18 '22 15:09

svick