I've always assumed that if a method should return a value, then if the method body doesn't return one, the program won't compile. In the case of a method with the async keyword, this is not the case. I thought that the async keyword allows a method in the body to have an await expression. But it also allows the body of the method to spit on the return value.
Do I understand correctly that a method with the async keyword is an exception to the rule about a mandatory return value?
Task MyMethod1() //Error CS0161 'MyMethod1()': not all code paths return a value
{
}
async Task MyMethod2() //Do not care
{
}
CodePudding user response:
You are correct that this is an exception. Both void
and async Task
methods have the effective return type of void
, and doesn't need a return
statement. This is specified in the language spec:
(emphasis mine)
The effective return type of a method is
void
if the return type isvoid
, or if the method isasync
and the return type isSystem.Threading.Tasks.Task
. Otherwise, the effective return type of a non-async method is its return type, and the effective return type of anasync
method with return typeSystem.Threading.Tasks.Task<T>
isT
.When the effective return type of a method is
void
and the method has a block body, return statements (§12.10.5) in the block shall not specify an expression. If execution of the block of a void method completes normally (that is, control flows off the end of the method body), that method simply returns to its caller.When the effective return type of a method is not void and the method has a block body, each return statement in that method’s body shall specify an expression that is implicitly convertible to the effective return type. The endpoint of the method body of a value-returning method shall not be reachable.
And thinking about it intuitively, even if they wanted to design this so that you must return an instance of Task
, what Task
would you return? There is nothing that makes sense to return! The task that an async method returns is supposed to be representing the asynchronous operations that it carries out.
What actually happens is, some compiler magic turns the code in your method body into a Task
, and make your method return that instead. See also: How does async works in C#?
CodePudding user response:
The async
/ await
keywords trigger a whole bunch of magic in the C# compiler. Take a "simple" example;
async Task MyMethod2()
{
Console.WriteLine("Before");
await Task.Delay(1000);
Console.WriteLine("After");
}
The goal of the compiler is to rewrite your method so it can pause at each await
and resume later. It moves your method to a new type, using instance fields to track where your method is paused, and the value of any local variables. All so that you can pretend your method is closer to;
void MyMethod2()
{
Console.WriteLine("Before");
var task = Task.Delay(1000);
if (!task.IsCompleted) {
... pause / resume magic ...
}
Console.WriteLine("After");
}
You can see all the gory details with a decompiler.
CodePudding user response:
No it's not an exception. The returned Task
object can be captured and used as any other return value.
async Task F1Async()
{
await Task.Delay(TimeSpan.FromMilliseconds(200));
}
async Task F2Async()
{
Task t1 = F1Async();
// Do other things if you wish here.
await t1;
}