异步无效 lambda 表达式

Async void lambda expressions

快点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。我的问题基本上是这个最佳实践的一个分支:

下面的 lambda 表达式的计算结果是什么?

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

如果它成为 async Task,那么我们将遵循最佳实践。

但是如果计算结果为 async void 怎么办?

是的,它被评估为 async Task,因为 Task.Delay(n) 具有 return 类型的 Task。所以这是很好的做法。

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);
    }
}

所以上面的代码可以缩短为:

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

现在缩短的代码看起来像您的代码。

The documentation 对于表达式 lambda 表示,

An expression lambda returns the result of the expression

因此,例如,() => "hi" return 是一个字符串,即使没有 return 语句。但是如果表达式没有 return 任何东西,比如 () => Console.WriteLine("hi"),那么它被认为是 void.

然而,async lambda 有一些技巧。表达式 await Task.Delay(1000) 本身并没有真正 return 任何东西。然而,该语言可以计算出如果你有一个 async lambda,你可能希望它成为 return 一个 Task。所以它会更喜欢那个。

所以这个:

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

相当于这个,如果你用命名方法来表达它:

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

但需要注意的是,async lambdas 可以 推断为 async void。这里被认为是 async Task 的唯一原因是因为 Task.Run has an overload for Func<Task>. If the only available overload took an Action 参数,那么它会被推断为 async void,而不会给你任何警告。

例如,这不会产生错误并且 lambda 被视为 async void:

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

这与向其传递命名的 async Task 方法不同,后者会导致编译器错误:

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

所以在使用它的地方要小心。您始终可以将鼠标悬停在方法名称上(如 Task.Run 中的 Run),Visual Studio 会告诉您它推断出哪个重载: