我在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
时为什么不起作用?
结论:
好的,现在我明白了,结论是:
PerformClick
,切勿调用async
。它不会await
!剩下的就是缺乏关于此的文档:
async
应该在doc页面上有一个警告:await
“调用PerformClick不会等待异步事件处理程序。”
Button.PerformClick
方法(或eventhandler)应该给编译器警告:“你正在调用异步void方法,它将不会被等待!”您似乎对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_Click
和async
中等待它。这正是你在你的button1_Click
所做的,但要注意button2_Click
也需要有一个"Edit" section签名。
1如果您认为答案是别的,那么您可能需要检查解释警告的button2_Click
。
2唯一的区别是,在第一种情况下,我们可以通过等待方法来解决问题,但是,在第二种情况下,我们不能这样做,因为“方法”不等待async
,即使它有this article签名。
3实际上,两者之间存在一些差异(例如,because the return type is void
中的验证逻辑,但这些差异不会影响我们目前情况下的最终结果。