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

col17t5w  于 2022-11-16  发布在  其他
关注(0)|答案(1)|浏览(182)

我在使用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
剩下的就是缺少相关文档:

  1. Button.PerformClick应在文档页面上显示警告
    Button.PerformClick调用PerformClick将不等待异步事件处理程序。
    1.调用async void方法(或事件处理程序)应给予编译器警告:“你调用的是异步虚空法,不等了!
nnt7mjpx

nnt7mjpx1#

您 似乎 对 async/await 和/或 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.
}

中 的 每 一 个
现在 , 您 希望 MessageBox 显示 什么 呢 ? 您 可能 会 说 " started " 。 1 为什么 ? 因为 我们 没有 等待 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.
}

格式
现在 , 我 所 做 的 只是 将 * async * 方法 MyMethodAsync() 中 的 代码 移动 到 * async * 事件 处理 程序 button1_Click 中 , 然后 使用 button1_Click(null, null) 调用 该 事件 处理 程序 。在 这 两 种 情况 下 , 我 都 调用 了 异步 方法 * * 而 没有 等待 它 * * 。 2
如果 你 同意 我 的 观点 , 那么 你 可能 已经 明白 为什么 你 的 代码 不能 像 预期 的 那样 工作 了 。 上面 的 代码 ( 在 第 二 个 例子 中 ) 和 你 的 代码 几乎 是 一样 的 。 不同 的 是 我 使用 了 button1_Click(null, null) 而 不是 button1.PerfomClick() , 它 本质 上 做 了 同样 的 事情 。 3

# # 解决 方案 :

如果 要 等待 button1_Click 中 的 代码 完成 ,您 需要 移动 button1_Click * 中 的 所有 内容( 只要 它 是 异步 代码 ) * 导入 到 async 方法 中 , 然后 * * 在 * * button1_Clickbutton2_Click 中 * * 等待 * * 它 。这 正是 您 在 "Edit" section 中 所 做 的 , 但 请 注意 , button2_Click 也 需要 有 async 签名 。
1 如果 您 认为 答案 是 其他 内容 , 则 可能 需要 选中 this article , 它 解释 了 警告 。
2 唯一 的 区别 是 , 在 第 一 种 情况 下 , 我们 可以 通过 等待 方法 来 解决 问题 , 但 在 第 二 种 情况 下 , 我们 不能 这样 做 , 因为 " 方法 " 是 不可 等待 的 because the return type is void * , 即使 它 具有 async 签名 * 。
3 实际 上 , 两者 之间 存在 一些 差异 ( 例如 , PerformClick() 中 的 验证 逻辑 ) , 但 这些 差异 不会 影响 我们 当前 情况 下 的 最终 结果 。

相关问题