Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling BeginInvoke from a destructor

I have some code in a WPF application that looks like this:

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);
    }
}

The dispose method is never getting explicitly called so the destructor calls it. It seems like in this case the object would be destroyed before the delegate in the BeginInvoke fires on the UI thread. It appears to be working though. What is happening here? Is this safe?

like image 907
JonDrnek Avatar asked May 27 '15 14:05

JonDrnek


2 Answers

It seems like in this case the object would be destroyed before the delegate in the BeginInvoke fires on the UI thread

The finalizer queues work to the UI message loop. The object may finish running its finalizer method before the actual delegate gets invoked on the UI thread, but that doesn't matter, as the delegate gets queued regardless.

What is happening here?

You're queueing work to the UI from the finalizer.

Is this safe?

Safe is a broad term. Would I do this? Definitely not. It looks and feels weird that you're invoking manipulation of UI elements from a finalizer, especially given this is a TextBox control. I suggest you get the full grasp of what running a finalizer guarantees and doesn't guarantee. For one, running a finalizer doesn't mean the object gets cleaned up in memory right away.

I'd also suggest reading @EricLippert posts: Why everything you know is wrong, Part1 & Part2

like image 149
Yuval Itzchakov Avatar answered Nov 13 '22 01:11

Yuval Itzchakov


When you call BeginInvoke you're adding a delegate to a queue in the dispatcher, and that delegate is going to be pointing to an object that has a reference to the object that Dispose was called on. Since there is a reference to the object accessible through a rooted variable, this object is not eligible for collection.

Now, this is going to be extremely confusing to others using this code, so you should try to avoid "reanimating" objects that have already been finalized if at all possible.

like image 3
Servy Avatar answered Nov 13 '22 02:11

Servy