18

In C#, when you override a method, it is permitted to make the override async when the original method was not. This seems like poor form.

The example that brought me to this was this — I was brought in to assist with a load test problem. At around 500 concurrent users, the login process would break down in a redirect loop. IIS was logging exceptions with the message "An asynchronous module or handler completed while an asynchronous operation was still pending". Some searching led me to think that someone was abusing async void, but my quick searches through the source could find nothing.

Sadly, I was searching for 'async\svoid' (regex search) when I should have been looking for something more like 'async\s[^T]' (assuming Task wasn't fully qualified… you get the point).

What I later found was async override void onActionExecuting(... in a base controller. Clearly that had to be the problem, and it was. Fixing that up (making it synchronous for the moment) resolved the problem.

Back to the question: Why oh why can you mark an override as async when the calling code could never await it?

2 Answers2

18

The async keyword allows the method to use the await syntax within its definition. I can await on any method that returns a Task type regardless of whether it's an async method.

void is a legal (albeit discouraged) return type for an async method, so why wouldn't it be allowed? From the outside, async isn't enabling anything you couldn't do without it. The method you are having trouble with could have been written to behave exactly the same without being async. Its definition just would have been more verbose.

To callers, an async T method is a normal method that returns T (which is limited to void,Task, or Task<A>). That it is an async method is not part of the interface. Notice that the following code is illegal:

interface IFoo {
    async void Bar();
}

It (or similar code in an abstract class) produces the following error message in VS2012:

The 'async' modifier can only be used in methods that have a statement body

If I did intend a method in an interface or parent class to be typically asynchronous, I can't use async to communicate that. If I wanted to implement it with the await syntax, I would need be able to have async override methods (in the parent class case).

Matthew Flynn
  • 13,495
  • 2
  • 39
  • 58
13

The sole purpose of the async keyword is to make await within the body of that function a keyword. This is needed so that adding the await functionality did not break existing code. Microsoft decided to make use of await an opt-in option. For a function that is not marked async you can define a variable named await with no issue.

Hence async is not part of a function's signature and has no semantical meaning in the generated IL code. It is strictly there for the compiler to know how to to correctly compile the function. It also instructs the compiler to ensure that only Task, Task<T> or void is being returned.