异步无返回值的lambda表达式

17

快速的谷歌搜索会告诉你,在可能的情况下要避免使用async void myMethod()方法。并且在许多情况下,有方法使其成为可能。我的问题基本上是这个最佳实践的一个分支:

下面的 lambda表达式评估为什么?

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

如果变成 async Task,那么我们就遵循最佳实践。

但如果变成 async void 呢?


2
这里似乎有一些解释:http://www.thebillwagner.com/blog/Item/2016-05-18-DoasynclambdasreturnTasks。在您的特定情况下,它将解析为返回“Task”的异步函数,因此遵循最佳实践。 - Corentin Pane
3个回答

24

表达式Lambda的文档说:

一个表达式Lambda返回表达式的结果。

例如,() => "hi"返回一个字符串,即使没有return语句。但是,如果表达式没有返回任何内容,如 () => Console.WriteLine("hi"),则被视为void

但是,对于async lambdas会有一些诡异的地方。表达式await Task.Delay(1000)本身并没有返回任何东西。然而,编程语言可以推断出,如果你有一个async lambda,你可能希望它返回一个Task。所以它会优先这样做。

因此,下面的代码:

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

如果你用带有名称的方法表示,它等同于这个:

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

需要注意的是,async lambda表达式可能会被推断为async void。在这里被视为async Task的唯一原因是因为Task.Run有一个重载的Func<Task>。如果唯一可用的重载使用了一个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将告诉您它已推断出哪个重载:

enter image description here


4

是的,它被评估为 async Task,因为 Task.Delay(n) 的返回类型是 Task。所以这是一个好的实践方法。

此外,还有一个微软官方文档上的例子,但它有点啰嗦

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

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


0
根据文档所说,从C# 10开始,你可以在输入参数之前指定lambda表达式的返回类型。
var fn = async Task (string p1, int p2) =>
{
    // await something
};

在使用Task.Run(..)时,请参考Gabriel Luci的答案。但是我通常会明确指定Task,以便在asp.net core的Map路由方法中更清晰明了。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接