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?

4
  • 3
    please don't cross-post: stackoverflow.com/questions/35817558/…
    – gnat
    Commented Mar 5, 2016 at 18:45
  • What exactly inhibits a caller to await the overridden method? I see no obstacle. Commented Mar 5, 2016 at 20:05
  • @MartinMaat You can only await methods that return Task. Commented Mar 5, 2016 at 20:08
  • Perhaps that's a fine point I had not noticed. I'd have to check, but I don't recall VS warning about invoking non-async methods without await. Commented Mar 5, 2016 at 20:16

2 Answers 2

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).

10
  • 1
    You don't mention override at all, but that's what the question is about. Commented Mar 5, 2016 at 19:47
  • 6
    @NathanTuggy The whole point of the answer is that async doesn't change the interface of a method, only what is syntactically allowed in its definition, so whether it's an override or not is completely irrelevant. Commented Mar 5, 2016 at 19:51
  • 2
    Please explain that, rather than assuming everyone already knows everything relevant. Commented Mar 5, 2016 at 19:54
  • 2
    @NathanTuggy I feel I have explained it. Can you ask a specific question about which you are confused or you imagine another confused person would ask? The only thing I can think to add is to point out that the async modifier can only be applied to implementations. You can't declare a method async in an interface, for example, underscoring that whether a method is async or not is not part of the interface. Commented Mar 5, 2016 at 20:06
  • 6
    Put the explanation in the answer, I mean. That's not what comments are for in the long run. Commented Mar 5, 2016 at 20:11
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.

1
  • This was very useful to understand the design decision and the implications it causes. Commented Jul 17, 2020 at 14:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.