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?

Nathan Tuggy
  • 345
  • 1
  • 6
  • 14
  • 3
    please don't **[cross-post](http://meta.stackexchange.com/tags/cross-posting/info "'Cross-posting is frowned upon...'")**: http://stackoverflow.com/questions/35817558/why-does-c-sharp-allow-you-you-make-an-override-async – gnat Mar 05 '16 at 18:45
  • What exactly inhibits a caller to await the overridden method? I see no obstacle. – Martin Maat Mar 05 '16 at 20:05
  • @MartinMaat You can only await methods that return `Task`. – Derek Elkins left SE Mar 05 '16 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. – Peter T. LaComb Jr. Mar 05 '16 at 20:16

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,345
  • 2
  • 38
  • 57
Derek Elkins left SE
  • 6,591
  • 2
  • 13
  • 21
  • 1
    You don't mention `override` at all, but that's what the question is about. – Nathan Tuggy Mar 05 '16 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. – Derek Elkins left SE Mar 05 '16 at 19:51
  • 2
    Please *explain* that, rather than assuming everyone already knows everything relevant. – Nathan Tuggy Mar 05 '16 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. – Derek Elkins left SE Mar 05 '16 at 20:06
  • 6
    Put the explanation *in the answer*, I mean. That's not what comments are for in the long run. – Nathan Tuggy Mar 05 '16 at 20:11
  • @NathanTuggy How are you expecting people to understand advanced topic around virtual methods without knowing basics? He doesn't need to explain what override does, when that is basic knowledge when working with C#. – Euphoric Mar 06 '16 at 07:17
  • 1
    @Euphoric: If I missed it, others might. And I *do* know what override does, in general. I also know what async does, in general. So I'm a perfect test case: I know more than enough about C# to benefit from the answer, but when I read it, I had no idea what the answer was trying to say. It's great when an answer is terribly clever and elliptical and tersely gets across all it needs to in about five words. It's not so great when it *doesn't* get across what it needs to. If you have to know the answer to the question to understand the answer to the question, it doesn't work so well. That's all. – Nathan Tuggy Mar 06 '16 at 07:21
  • @NathanTuggy I'm skeptical of your claim that you understand async. Knowing how overriding and async (with emphasis on void return type) works creates clear and correct inference on how those two work together. – Euphoric Mar 06 '16 at 07:26
  • 1
    @Euphoric: Obviously I didn't understand *enough*. That's what questions are for. That's why answers are written: to fill in the gaps in knowledge. Had I understood everything involved, I would have been able to write the answer myself; had the asker of this question, they would not have needed to ask anything. It's a great pity so many people have such imperfect understanding; otherwise we could get rid of this site, with all its unnecessary re-explaining of basic facts that everyone should already know! – Nathan Tuggy Mar 06 '16 at 07:47
  • @Nathan, Derek makes the point that the same question/issue that the OP has about overrides also applies to implementing an interface (which doesn't require overrides). The question is specifically about overrides but the answer here is necessarily more general. – Erik Eidt Mar 06 '16 at 17:00
12

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.

Eric Johnson
  • 444
  • 2
  • 5