Button.PerformClick()不等待异步事件处理程序

问题描述 投票:1回答:1

我在Windows窗体应用程序中遇到问题,我使用PerformClick来调用async事件处理程序。似乎事件处理程序不是await但只是立即返回。我创建了这个简单的应用程序来显示问题(它只是一个带有3个按钮的表单,很容易自己测试):

string message = "Not Started";

private async void button1_Click(object sender, EventArgs e)
{
    await MyMethodAsync();
}

private void button2_Click(object sender, EventArgs e)
{
    button1.PerformClick();
    MessageBox.Show(message);
}

private void button3_Click(object sender, EventArgs e)
{
    MessageBox.Show(message);
}

private async Task MyMethodAsync()
{
    message = "Started";
    await Task.Delay(2000);
    message = "Finished";
}

这里有趣的问题是,当我点击message时,你认为Button2显示什么?令人惊讶的是,它显示了“已开始”,而不是“完成”,正如您所期望的那样。换句话说,它不是await MyMethod(),它只是启动任务,然后继续。

编辑:

在这个简单的代码中,我可以通过直接从Button2事件处理程序调用await Method()来使其工作,如下所示:

private async void button2_Click(object sender, EventArgs e)
{
    await MyMethodAsync();
    MessageBox.Show(message);
}

现在,它等待2秒并显示“已完成”。

这里发生了什么?使用PerformClick时为什么不起作用?

结论:

好的,现在我明白了,结论是:

  1. 如果事件处理程序是PerformClick,切勿调用async。它不会await
  2. 永远不要直接调用qazxsw poi事件处理程序。它不会qazxsw poi!

剩下的就是缺乏关于此的文档:

  1. async应该在doc页面上有一个警告:

await“调用PerformClick不会等待异步事件处理程序。”

  1. 调用Button.PerformClick方法(或eventhandler)应该给编译器警告:“你正在调用异步void方法,它将不会被等待!”
c# winforms async-await eventhandler
1个回答
4
投票

您似乎对Button.PerformClick和/或async void如何工作有一些误解。要说明问题,请考虑以下代码:

async/await

注意:编译器会给我们一个警告,但为了测试,我们忽略它。

现在,您期望MessageBox显示什么?你可能会说“开始”.1为什么?因为我们没有等待PerformClick()方法;该方法中的代码异步运行但我们没有等待它完成,我们只是继续到下一行,其中private async Task MyMethodAsync() { await Task.Delay(2000); message = "Finished"; // The execution of this line will be delayed by 2 seconds. } private void button2_Click(object sender, EventArgs e) { message = "Started"; MyMethodAsync(); // Since this method is not awaited, MessageBox.Show(message); // the execution of this line will NOT be delayed. } 的值尚未更改。

如果您了解到目前为止的行为,其余的应该很容易。那么,让我们稍微改变上面的代码:

MyMethodAsync()

现在,我所做的就是将异步方法message中的代码移动到异步事件处理程序private async void button1_Click(object sender, EventArgs e) { await Task.Delay(2000); message = "Finished"; // The execution of this line will be delayed by 2 seconds. } private void button2_Click(object sender, EventArgs e) { message = "Started"; button1_Click(null, null); // Since this "method" is not awaited, MessageBox.Show(message); // the execution of this line will NOT be delayed. } 中,然后使用MyMethodAsync()调用该事件处理程序。这和第一个代码有区别吗?不,这基本上是一回事;在这两种情况下,我都在不等待它的情况下调用异步方法

如果您同意我的意见,您可能已经理解为什么您的代码无法按预期工作。上面的代码(在第二种情况下)几乎与你的相同。区别在于我使用button1_Click而不是button1_Click(null, null),它基本上做同样的事情.3

解决方案:

如果你想等待button1_Click(null, null)中的代码完成,你需要将button1.PerfomClick()中的所有内容(只要是异步代码)移动到button1_Click方法中,然后在button1_Clickasync中等待它。这正是你在你的button1_Click所做的,但要注意button2_Click也需要有一个"Edit" section签名。


1如果您认为答案是别的,那么您可能需要检查解释警告的button2_Click

2唯一的区别是,在第一种情况下,我们可以通过等待方法来解决问题,但是,在第二种情况下,我们不能这样做,因为“方法”不等待async,即使它有this article签名。

3实际上,两者之间存在一些差异(例如,because the return type is void中的验证逻辑,但这些差异不会影响我们目前情况下的最终结果。

© www.soinside.com 2019 - 2024. All rights reserved.