从析构函数中调用BeginInvoke

本文关键字:BeginInvoke 调用 析构函数 | 更新日期: 2023-09-27 17:50:51

我在WPF应用程序中有一些代码,看起来像这样:

public class MyTextBox : System.Windows.Controls.TextBox, IDisposable
{
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    protected virtual void Dispose(bool disposing)
    {
        Dispatcher.BeginInvoke((Action) delegate
        {
            // do work on member variables on the UI thread.
        });
    }
    ~MyTextBox()
    {
        Dispose(false);
    }
}

dispose方法永远不会被显式调用,因此析构函数调用它。在这种情况下,对象似乎会在BeginInvoke中的委托在UI线程上触发之前被销毁。不过,它似乎正在起作用。这里发生了什么?这安全吗?

从析构函数中调用BeginInvoke

在这种情况下,对象似乎会在在UI线程

终结器队列为UI消息循环工作。对象可以在实际委托在UI线程上被调用之前完成其终结器方法的运行,但这无关紧要,因为委托无论如何都会排队。

这是怎么回事?

你正在将工作从终结器排队到UI。

这安全吗?

Safe是一个广义的术语。我会这么做吗?绝对不会。从终结器调用UI元素的操作看起来和感觉都很奇怪,特别是考虑到这是一个TextBox控件。我建议您充分了解运行终结器可以保证什么,不能保证什么。首先,运行终结器并不意味着对象会立即在内存中被清理。

我还建议阅读@EricLippert的帖子:为什么你知道的一切都是错的, Part1 &第二部分

当你调用BeginInvoke时,你正在向调度程序中的队列添加一个委托,并且该委托将指向一个对象,该对象引用了Dispose被调用的对象。由于存在通过根变量可访问的对象的引用,因此该对象不符合收集条件。

现在,这会让使用这段代码的其他人非常困惑,所以您应该尽量避免"重新激活"已经完成的对象。