【问题标题】:Button.DialogResult, Form.ShowDialog and async event handlers out of -expected- order executionButton.DialogResult、Form.ShowDialog 和异步事件处理程序超出了预期的执行顺序
【发布时间】:2021-05-17 20:58:06
【问题描述】:

我会尽量简短。两种形式:

Form1 (button1, textBox1)


public partial class Form1 : Form
{
    private readonly Form2 fr2 = new Form2();

    public Form1()
    {
        InitializeComponent();
        fr2.textBox1 = textBox1;
    }

    private async void button1_Click(object sender, EventArgs e)
    {
        textBox1.Text = string.Empty;
        if (fr2.ShowDialog() == DialogResult.OK)
        textBox1.AppendText("Form1" + Environment.NewLine);
    }
}

Form2(带有 button1.DialogResult == DialogResult.OK 的 button1)


public partial class Form2 : Form
{
    public TextBox textBox1;

    public Form2()
    {
        InitializeComponent();
    }

    private async void button1_Click(object sender, EventArgs e)
    {
        textBox1.AppendText("Form2 A" + Environment.NewLine);
        await Task.Delay(1000);
        textBox1.AppendText("Form2 B" + Environment.NewLine);
        Hide();
    }
}

  • 运行
  • 点击Form1.Button1
  • 点击Form2.Button1

为什么是 Textbox.Text ==

@"Form2 A
Form1
Form2 B
";

而不是

@"Form2 A
Form2 B
Form1
"?

为什么表单甚至没有完成事件处理程序就“返回”给调用者并稍后完成?

这是记录在案的行为还是错误?

更新:

显然,如果 button.DialogResult 是预设的,原始 winform 的表单事件处理代码未更新以考虑异步事件处理程序,它只是被视为“触发并忘记”异步方法调用。我必须研究更多,看看如果抛出异常会发生什么。似乎异步机器也被“解雇并忘记了”(没有Task 保存以供以后使用)?!这是与串行案例非常不同的行为,并且在任何地方都没有记录。现场评论await,Hans Passant!

【问题讨论】:

  • 我假设 Form1 中的代码应该添加“Form1 A”而不仅仅是“Form1”?
  • 因为await这个关键字引发了一百万个bug。将按钮的 DialogResult 属性设置回 None,这样您的代码就可以控制对话框的生命周期。不要使用 Hide(),取消对话框,使用 this.DialogResult = DialogResult.OK;
  • @JackT.Spades 是的。你说的对。剪切、粘贴和编辑不是避免错误的好方法;-) 已修复

标签: c# winforms asynchronous async-await


【解决方案1】:

这是预期行为,因为您异步等待 Task.Delay 任务完成。在此过程中,该方法返回给它的调用者,这是设计使然。举个例子:

private void button1_Click(object sender, EventArgs e)
{
   method();
   textBox1.Text += "C";
}

async void method()
{
   textBox1.Text += "A";
   await Task.Delay(1000);
   textBox1.Text += "B";
}

这几乎是相同的情况,TextBox 的文本将在按下 button1 后为 ACB,因为方法 method() 在调用 await Task.Delay(1000); 时返回给它的调用者。

要获得所需的输出,请按照 cmets 中 Hans Passant 的建议进行操作:将按钮的 DialogResult 属性设置为 None,以便该方法不会返回到 Form1。然后,当您希望再次将控制权交给Form1 时,只需在运行时的方法中调用this.DialogResult = DialogResult.OK 即可,效果相同。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-15
    • 2011-10-27
    • 1970-01-01
    • 1970-01-01
    • 2018-07-13
    相关资源
    最近更新 更多