我在使用PerformClick
调用async
事件处理程序的Windows窗体应用程序中遇到了问题。事件处理程序似乎不是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";
}
这里有趣的问题是,当我点击Button2
时,你认为message
会显示什么?**令人惊讶的是,**它显示“Started”,而不是你所期望的“Finished”。换句话说,它不是await MyMethod()
,它只是启动Task,然后继续。
编辑:
在这个简单的代码中,我可以通过直接从Button 2事件处理程序调用await Method()
来使其工作,如下所示:
private async void button2_Click(object sender, EventArgs e)
{
await MyMethodAsync();
MessageBox.Show(message);
}
现在,它等待2秒并显示“完成”。
这是怎么回事?为什么在使用PerformClick
时不起作用?
结论:
好了,现在我明白了,结论是:
1.如果事件处理程序是async
,则永远不要调用PerformClick
。它不会是await
!
1.永远不要直接调用async
事件处理程序。它不会调用await
!
剩下的就是缺少相关文档:
Button.PerformClick
应在文档页面上显示警告:
Button.PerformClick“调用PerformClick将不等待异步事件处理程序。”
1.调用async void
方法(或事件处理程序)应给予编译器警告:“你调用的是异步虚空法,不等了!”
1条答案
按热度按时间nnt7mjpx1#
您 似乎 对
async/await
和/或PerformClick()
的 工作 原理 有 一些 误解 。 为了 说明 这个 问题 , 请 考虑 以下 代码 :中 的 每 一 个
现在 , 您 希望 MessageBox 显示 什么 呢 ? 您 可能 会 说 " started " 。 1 为什么 ? 因为 我们 没有 等待
MyMethodAsync()
方法 ;该 方法 中 的 代码 * 异步 * 运行 , 但 我们 没有 * 等待 * 它 完成 , 我们 只是 继续 到 下 一 行 , 其中message
的 值 尚未 更改 。如果 你 已经 理解 了 这个 行为 , 剩下 的 就 很 容易 了 。 所以 , 让 我们 稍微 修改 一下 上面 的 代码 :
格式
现在 , 我 所 做 的 只是 将 * async * 方法
MyMethodAsync()
中 的 代码 移动 到 * async * 事件 处理 程序button1_Click
中 , 然后 使用button1_Click(null, null)
调用 该 事件 处理 程序 。在 这 两 种 情况 下 , 我 都 调用 了 异步 方法 * * 而 没有 等待 它 * * 。 2如果 你 同意 我 的 观点 , 那么 你 可能 已经 明白 为什么 你 的 代码 不能 像 预期 的 那样 工作 了 。 上面 的 代码 ( 在 第 二 个 例子 中 ) 和 你 的 代码 几乎 是 一样 的 。 不同 的 是 我 使用 了
button1_Click(null, null)
而 不是button1.PerfomClick()
, 它 本质 上 做 了 同样 的 事情 。 3# # 解决 方案 :
如果 要 等待
button1_Click
中 的 代码 完成 ,您 需要 移动button1_Click
* 中 的 所有 内容( 只要 它 是 异步 代码 ) * 导入 到async
方法 中 , 然后 * * 在 * *button1_Click
和button2_Click
中 * * 等待 * * 它 。这 正是 您 在 "Edit" section 中 所 做 的 , 但 请 注意 ,button2_Click
也 需要 有async
签名 。1 如果 您 认为 答案 是 其他 内容 , 则 可能 需要 选中 this article , 它 解释 了 警告 。
2 唯一 的 区别 是 , 在 第 一 种 情况 下 , 我们 可以 通过 等待 方法 来 解决 问题 , 但 在 第 二 种 情况 下 , 我们 不能 这样 做 , 因为 " 方法 " 是 不可 等待 的 because the return type is
void
* , 即使 它 具有async
签名 * 。3 实际 上 , 两者 之间 存在 一些 差异 ( 例如 ,
PerformClick()
中 的 验证 逻辑 ) , 但 这些 差异 不会 影响 我们 当前 情况 下 的 最终 结果 。