Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Async void lambda expressions

A quick google search will tell you to avoid using async void myMethod() methods when possible. And in many cases there are ways to make it possible. My question is basically an offshoot of this best practice:

What does the lambda expression below evaluate to?

Task.Run( async ()=> await Task.Delay(1000));

If it becomes an async Task then we are following best practice.

But what if it evaluates to async void?

like image 979
bwall Avatar asked May 15 '20 20:05

bwall


People also ask

What is lambda expression in Linq?

Advertisements. The term 'Lambda expression' has derived its name from 'lambda' calculus which in turn is a mathematical notation applied for defining functions. Lambda expressions as a LINQ equation's executable part translate logic in a way at run time so it can pass on to the data source conveniently.

What are lambda expressions C#?

C# lambda expression is a syntax to create delegates or expression trees. It is a very powerful syntactic sugar making C# functional.


2 Answers

The documentation for expression lambdas says,

An expression lambda returns the result of the expression

So, for example, () => "hi" returns a string, even though there is no return statement. But if the expression doesn't return anything, like in () => Console.WriteLine("hi"), then it's considered void.

However there is a bit of trickery with async lambdas. The expression await Task.Delay(1000) doesn't really return anything in itself. However, the language can figure out that if you have an async lambda, you likely want it to return a Task. So it will prefer that.

So this:

Task.Run(async () => await Task.Delay(1000));

Is equivalent to this, if you were to express it with a named method:

private async Task Wait1000() {
    await Task.Delay(1000);
}
Task.Run(Wait1000);

But it is important to note that async lambdas can be inferred to be async void. The only reason it is considered async Task here is because Task.Run has an overload for Func<Task>. If the only available overload took an Action parameter, then it would be inferred to be async void, without any warning to you.

For example, this produces no error and the lambda is treated as async void:

private void RunThisAction(Action action) {
    action();
}
RunThisAction(async () => await Task.Delay(1000));

That is different than if you passed it a named async Task method, which would cause a compiler error:

private void RunThisAction(Action action) {
    action();
}
private async Task Wait1000() {
    await Task.Delay(1000);
}
RunThisAction(Wait1000); // 'Task Wait1000()' has the wrong return type

So be careful where you use it. You can always hover over the method name (like the Run in Task.Run) and Visual Studio will tell you which overload it has inferred:

enter image description here

like image 61
Gabriel Luci Avatar answered Sep 28 '22 06:09

Gabriel Luci


Yeah, it is evaluated to async Task because Task.Delay(n) has return type of Task. So it is good practice.

In addition, there is msdn example, but it is a little bit more verbose:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        button1.Click += async (sender, e) =>
        {
            await ExampleMethodAsync();
            textBox1.Text += "\r\nControl returned to Click event handler.\n";
        };
    }

    private async Task ExampleMethodAsync()
    {
        // The following line simulates a task-returning asynchronous process.
        await Task.Delay(1000);
    }
}

So the above code could be shortened to:

public Form1()
{
    InitializeComponent();
    button1.Click += async (sender, e) =>
    {
        await Task.Delay(1000);
        textBox1.Text += "\r\nControl returned to Click event handler.\n";
        };
    }
}

And now shortened code looks like your code.

like image 35
StepUp Avatar answered Sep 28 '22 07:09

StepUp