-2

Maybe this is due to my lack of understanding of what happens during compilation when a method is marked async, but why does this method compile?

    public async Task<bool> Test()
    {
        return true;
    }

Just looking for an explanation here, so I can better understand what is going on. Is a Task automatically getting wrapped? Why is this method allowed? (it does not adhere to the method signature, which is to return a Task<bool>).

Update: It appears that this is also applicable for parameters to:

    public void Main()
    {
        Input(Test);
    }

    public async Task<bool> Test()
    {
        return true;
    }

    public void Input(Func<Task<bool>> test)
    {

    }

where, the Test method returns a Task implicitly.

d.moncada
  • 16,900
  • 5
  • 53
  • 82
  • Possible duplicate of [How Async and Await works](https://stackoverflow.com/questions/22349210/how-async-and-await-works) – Rufus L Oct 23 '17 at 22:12
  • You aren't required to `await` something in an `async` method. Is that what you're asking? – Rufus L Oct 23 '17 at 22:13
  • @RufusL right I understand that. but i do not understand why method 1 does not have to return a Task (which, based on its signature it should) – d.moncada Oct 23 '17 at 22:15
  • [Async return types](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/async-return-types) return the result of a task, not the task itself. In your example, the `async Task` returns a `bool`. – Rufus L Oct 23 '17 at 22:21
  • 4
    Your question is a bit confusing. Suppose you had `async Task FooAsync() { await Whatever(); return true; }` would you also ask why that method was compiling? I think you might not understand that a `Task` `return`s a `T` in an async method; `return t;` means *complete the task with this T* in an async method, just as `return t;` means *complete this method with a T* in non-async code. – Eric Lippert Oct 23 '17 at 23:36
  • @EricLippert actually, you are correct. I would not have questioned it if the method body itself was awaiting on something. thanks for providing a better understanding – d.moncada Oct 23 '17 at 23:56

1 Answers1

4

You can take a look at the decompiled version of your code here

using System;
public class C {
    public async System.Threading.Tasks.Task<bool> M() {
        return false;
    }
}

The method is compiled into a normal async one, with the state machine, it does return a Task<bool> after all.

public class C
{
    [CompilerGenerated]
    private sealed class <M>d__0 : IAsyncStateMachine
    {
        public int <>1__state;

        public AsyncTaskMethodBuilder<bool> <>t__builder;

        public C <>4__this;

        //called when you awaiting the Task
        void IAsyncStateMachine.MoveNext()
        {
            int num = this.<>1__state;
            bool result;
            try
            {
                result = false;  //the result is set
            }
            catch (Exception arg_0C_0)
            {
                Exception exception = arg_0C_0;
                this.<>1__state = -2;
                this.<>t__builder.SetException(exception);
                return;
            }
            this.<>1__state = -2;
            this.<>t__builder.SetResult(result); 
        }

        [DebuggerHidden]
        void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
        {
        }
    }

    [DebuggerStepThrough, AsyncStateMachine(typeof(C.<M>d__0))]
    public Task<bool> M()
    {
        // the state machine instance created
        C.<M>d__0 <M>d__ = new C.<M>d__0(); 
        <M>d__.<>4__this = this;
        <M>d__.<>t__builder = AsyncTaskMethodBuilder<bool>.Create();
        <M>d__.<>1__state = -1;
        AsyncTaskMethodBuilder<bool> <>t__builder = <M>d__.<>t__builder;
        <>t__builder.Start<C.<M>d__0>(ref <M>d__);
        return <M>d__.<>t__builder.Task;
    }
}

BTW, the compiler gives you a warning, because you never await in your method:

warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

And you're absolutely right, you may just return a completed task instead:

using System;
using System.Threading.Tasks;
public class C {
    public Task<bool> M() {
        return Task.FromResult(false); //no need to await here at all
    }

}

The other ways to code the method above could be found here.

using System;
using System.Threading.Tasks;
public class C {
    public async Task<bool> M() {
        return false;
    }

    public async Task<bool> M2(){
        return await Task.FromResult(false);
    }

    public Task<bool> M3(){
        return Task.FromResult(false);
    }
}

I hope this will clarify things for you.

Anton Sizikov
  • 9,105
  • 1
  • 28
  • 39
  • Not sure that I understand you. It does return a task. See the decompiled code. Even if you do `return false;` the actual state machine is built and the task returned. The caller may `await` on it, or just store/pass the task further. – Anton Sizikov Oct 23 '17 at 22:28
  • so, it implicitly returns a Task simply by adding the async keyword? Would compiled code be any different if I was to return Task.Run(()=>true) and remove the async keyword? – d.moncada Oct 23 '17 at 22:33
  • Please check the last link in my answer. When you `await` the whole state machine staff is built and initialized. Will all the performance/memory penalty :) If you just return a completed task (`Task.FromResult`) none of that is taking place. – Anton Sizikov Oct 23 '17 at 22:39
  • You can also play with the `Task.Run` there. You'll see that it's not really different, it's just another factory method for the `Task<>` object. – Anton Sizikov Oct 23 '17 at 22:40
  • And yes, the compiler changes the code so that it returns a `Task` when you add the `async` keyword. That's pretty much a syntax sugar for an old concept. Just less code to write. – Anton Sizikov Oct 23 '17 at 22:41
  • Long time ago we used to write a very chatty code for that https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/task-based-asynchronous-programming – Anton Sizikov Oct 23 '17 at 22:44