Question

Wondering why we should specify that async method does return Task object.

Specifying it seems redundant with the async keyword plus it is confusion since you do not really create the Task object.

As I understand the compiler does emit the necessary code for Task object creation (Whether on a await call or wrapping the return with a new Task.).

I don't really like the inconsistency between the declare type and the return Type.

Was it helpful?

Solution 3

I have a blog post that describes the reasoning in detail. Inferring the return type was considered but they decided not to.

With the explicit return type, the async keyword is more of an implementation detail. There are two reasons to have the explicit return type:

  • The method signature is consistent (it's the same at the point of declaration as when observed using IntelliSense or reflection).
  • There is a difference between async void and async Task. With an inferred return type, there's no clear way to define an async void method.

OTHER TIPS

This isn't really a question: it's more of a rant and therefore not particularly suitable for StackOverflow:

I don't really like the inconsistency between the declare type and the return type.

If you want to complain, start a blog and complain on that. Let's reformulate that as a question:

The declared type returned by an async method might be, say Task<int> but the expression returned by a return statement in that method must be implicitly convertible to int, not Task<int>. This is potentially confusing. What design principle justifies this behaviour?

You are right that this is potentially confusing. It is confusing because async methods separate two things that we are very used to thinking of as one thing. Those two things are:

  • What type of object is returned when control resumes in the caller of this method?
  • What type of object is passed from the method to its continuation? Remember that the continuation of a method is the code that runs when the method completes.

In synchronous methods those two things are always the same because the point of resumption in the caller is the continuation of a synchronous method. But the whole point of an asynchronous method is that the code in the caller is not the continuation of the method. The continuation of the method is controlled by setting the continuation of the task associated with it.

That's why the declared return type and the type given to the return statement are different. The caller wants a Task<int> but the continuation of the method wants an int. The return statement means "this method is done; give this value to my continuation" regardless of whether the method is synchronous or asynchronous.

It sounds like you're asking why public async int MyMethodAsync() isn't automatically compiled as a method that actually returns Task<int>.

The answer is the principle of least surprise: The declared return type in the method signature is always the actual return type.
This way, when you read a method declaration, you can always know the actual return type as seen when calling the method, without needing to look at modifiers and remember special rules.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top