我有2种形式,一种是MainForm,第二种是DebugForm。 MainForm有一个设置和显示DebugForm的按钮,并传递对已打开的SerialPort的引用:

private DebugForm DebugForm; //Field
private void menuToolsDebugger_Click(object sender, EventArgs e)
{
    if (DebugForm != null)
    {
        DebugForm.BringToFront();
        return;
    }

    DebugForm = new DebugForm(Connection);

    DebugForm.Closed += delegate
    {
        WindowState = FormWindowState.Normal;
        DebugForm = null;
    };

    DebugForm.Show();
}

在DebugForm中,我附加了一个方法来处理串行端口连接的DataReceived事件(在DebugForm的构造函数中):
public DebugForm(SerialPort connection)
{
    InitializeComponent();
    Connection = connection;
    Connection.DataReceived += Connection_DataReceived;
}

然后在Connection_DataReceived方法中,我在DebugForm中更新了一个TextBox,它使用Invoke进行了更新:
private void Connection_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    _buffer = Connection.ReadExisting();
    Invoke(new EventHandler(AddReceivedPacketToTextBox));
}

但是我有一个问题。一旦我关闭DebugForm,它就会在ObjectDisposedException行上抛出一个Invoke(new EventHandler(AddReceivedPacketToTextBox));

我怎样才能解决这个问题?欢迎任何提示/帮助!

更新

我发现是否在按钮事件click中删除了事件,然后在该按钮单击中关闭了表单,一切都很好,并且debugform毫无异常(exception)地被关闭了……多么奇怪!
private void button1_Click(object sender, EventArgs e)
{
    Connection.DataReceived -= Connection_DebugDataReceived;
    this.Close();
}

最佳答案

关闭表单将处理Form对象,但不能强行删除其他类对此对象的引用。在为事件注册表单时,基本上是在对事件源(在这种情况下为SerialPort实例)提供对表单对象的引用。

这意味着,即使您的表单已关闭,事件源(您的SerialPort对象)仍将事件发送到表单实例,并且处理这些事件的代码仍在运行。然后的问题是,当此代码尝试更新已处置的表单(设置其标题,更新其控件,调用Invoke和&c。)时,您将收到此异常。

因此,您需要做的是确保关闭表单后注销该事件。这就像检测到表单正在关闭并取消注册Connection_DataReceived事件处理程序一样简单。通过覆盖OnFormClosing方法并在那里注销事件,您可以方便地检测到表单正在关闭:

protected override OnFormClosing(FormClosingEventArgs args)
{
    Connection.DataReceived -= Connection_DataReceived;
}

我还建议将事件注册移至OnLoad方法的替代项,否则它可能会在表单完全构建之前接收事件,这可能会导致令人困惑的异常。

关于c# - 调用 `ObjectDisposedException`时避免 `Invoke`,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/12956288/

10-10 21:31