Syntony
Syntony

Reputation: 340

Conversion from async lambda Action into Func<Task>?

We know the async equivalent of Action is Func<Task>.

Therefore we could write:

Func<Task> func = async () =>
        {
            Console.WriteLine(@"waiting ...");
            await Task.Delay(300).ConfigureAwait(false);
            Console.WriteLine(@"... finished");
        };

But it is also possible to write it as an Action:

Action action = async () =>
        {
            Console.WriteLine(@"waiting ...");
            await Task.Delay(300).ConfigureAwait(false);
            Console.WriteLine(@"... finished");
        };

This is syntactically correct. How is it possible to convert Action action into Func<Task> func?

Upvotes: 4

Views: 1417

Answers (2)

quetzalcoatl
quetzalcoatl

Reputation: 33506

I think that the second snippet is similar to async void - just anonymous. You really should not use them except for some edge cases like WinForms event handlers..

The compiler manages the returned Task for you and puts it into default thread pool and runs continuations on some selected SynchroContext that is "known to be OK".. I don't remember the rest.. check the gory details of async-void and you will hit articles on why you shouldn't use it except for when absolutely necessary. for example:

Note for example differences in exception handling..

Aah, and I forgot to answer: If that's really async void then you cannot convert. Simply - the returned Task is gone and you can't obtain it. The Action you've got is something like:

Action action = () => 
{
    Func<Task> yourRealTask = async () =>
    {
        Console.WriteLine(@"waiting ...");
        await Task.Delay(300).ConfigureAwait(false);
        Console.WriteLine(@"... finished");
    };

    // ** some compiler-generated code to register/run the task somewhere
    // ** so it will be handled properly instea of being lost due to not
    // ** returning the Task to the caller

    return;
}

so, effectively, the yourRealTask is not obtainable.. with some luck you may hack it out of some closure via reflection, but.. don't do that. Just use Func<Task>.

Upvotes: 4

AgentFire
AgentFire

Reputation: 9780

The first case will create a normal Task factory.

The second will create a fire-and-forget async-void method, which is indeed used more rarely.

Since compiler is so neat, it can create both from a single anonymous method, and it's up to the programmer to decide which one he needs.

Upvotes: 1

Related Questions