48

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 problem that makes me wonder is this: I was brought in to assist with a load test problem. At around 500 concurrent users, the login process would break down into 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 found nothing.

Sadly, I was searching for async\s*void (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.

But it left me with a question: Why can you mark an override as async when the calling code could never await it?

21
  • 1
    async void is a correct pattern for event handlers. Just don't ignore warnings. Commented Mar 5, 2016 at 17:56
  • 1
    Not Meta, but programmers.SE. Commented Mar 5, 2016 at 17:58
  • 3
    I'm voting to close this question as off-topic because - based on comments I've re-posted to Programmers.SE: programmers.stackexchange.com/questions/311870/… Commented Mar 5, 2016 at 18:02
  • 12
    I do not think this question is offtopic. Understanding that async is not a part of a method signature is important programming knowledge. Even if it was offtopic, you should have not cross-posted it. Commented Mar 5, 2016 at 18:03
  • 2
    @PeterLaCombJr. The point of async is allowing you to use await inside of your method. It doesn't mean that your async method itself has to be awaited. So not being able to await an async method isn't a concern Commented Mar 5, 2016 at 18:38

3 Answers 3

41

When the base class (or interface) declares a virtual method that returns a Task, you can override it as long as you return Task. The async keyword is just a hint to the compiler to transform your method into a state machine. Although the compiler does its black magic on your method, the compiled method still returns a Task.


As for void virtual methods, you can override one without the async keyword (obviously) and start a non-awaited Task within it. That's kind of what happens when you override it with the async keyword and use await in the body. The caller would not await the created Task (since the "original" signature is void). Both cases are similar*:

public override void MyVirtualMethod() { // Will create a non awaited Task (explicitly) Task.Factory.StartNew(()=> SomeTaskMethod()); } public override async void MyVirtualMethod() { // Will create a non awaited Task (the caller cannot await void) await SomeTaskMethod(); } 

Stephen Cleary's article has some notes regarding this:

  • Void-returning async methods have a specific purpose: to make asynchronous event handlers possible.
  • You should prefer async Task to async void.

* The implementation of SomeTaskMethod, the underlying framework, the SynchronizationContext and other factors might and will cause different outcomes for each of the above methods.

Sign up to request clarification or add additional context in comments.

2 Comments

this - this is on topic. And in this case (task returning methods) the override being async might make sense. But not for void I think.
@PeterLaCombJr. Oh I missed the void part :) Please see my additional paragraph.
15

You can override async method because async is not a part of method signature. Actually async allows using await keyword in your method by creating a state machine inside.
You can find more info about async here: https://web.archive.org/web/20151001232244/http://blog.sublogic.com/2012/05/14/async-isnt-really-part-of-your-method-signature/

5 Comments

While the linked article is interesting, it says nothing about why async wasn't made part of the signature.
@PeterLaCombJr.it wouldn't make sense to have async Task Foo() and Task Foo() overloads.
True - but you can't distinguish methods by return type either.
@PeterLaCombJr. The point is that the only reason that the async keyword exists is to give await its special meaning, so that old code that uses a variable named "await" still compiles. Otherwise adding async changes nothing about the function.
Link in answer is dead - "403 Forbidden".
12

async isn't part of the "contract". It's an implementation detail that, in my opinion, unfortunately, appears in the wrong place.

It is perfectly legal to change a (non-async) method returning a Task into an async one (or vice versa) without that being a breaking change, and without requiring callers to recompile.

Further indication that it's not part of the contract is that you're not allowed to mark functions as async within interfaces and, as here, it's perfectly possible to override async with non-async or vice versa.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.