Freigeben über


How To Dispose Members From Forms

When you create a new Form (or UserControl for that matter), Visual Studio creates it as a partial class, with the designer-generated code going into the *.Designer.cs file. One other piece of code that also goes into the designer code is an override of System.ComponentModel.Component.Dispose(bool), which contains the code to dispose the form and its contained components. It always looks like this:

 protected override void Dispose(bool disposing)
{
    if (disposing && (components != null))
    {
        components.Dispose();
    }
    base.Dispose(disposing);
}

It's a bit unfortunate that this method is being overridden, because it makes it more difficult to add your own dispose logic to the form, since you can't override the method once more in the same class - remember that the form code that you control is only part of the class definition. You could obviously edit the designer file directly, but that's not recommended.

Then what can you do if you need to perform some extra dispose logic in your Form? This need can easily arrive if one of your member variables implement IDisposable, in which case you really should dispose of it when the Form disposes. Imagine you have the following IDisposable class:

 public class MyDisposable : IDisposable
{
    private string message_;
 
    public MyDisposable(string message)
    {
        this.message_ = message;
    }
 
    #region IDisposable Members
 
    public void Dispose()
    {
        MessageBox.Show(this.message_);
    }
 
    #endregion
}

You now want to have a MyDisposable member variable in your Form, and you need to dispose of it when the Form disposes:

 public partial class MyForm : Form
{
    private MyDisposable myDisposable_;
 
    public MyForm()
    {
        InitializeComponent();
 
        this.myDisposable_ =
            new MyDisposable("Goodbye, World");
    }
}

When you run the application and close the Form, the Form is being disposed, but you never see the goodbye message because myDisposable_ is not disposed. The Dispose method in the designer code handles the dispose logic for the form, and it knows nothing about the myDisposable_ member variable.

It does, however, Dispose of its components member, and since this is an IContainer, the problem would be solved if you could add myDisposable_ to the container. Unfortunately, IContainer only accepts IComponent instances, and if you may not want to implement IComponent on MyDisposable.

Part of the contract of IComponent is that an implementation must have a default constructor, and for API design reasons, you may not find that acceptable for your IDisposable type - for some reason, I don't think that's an acceptable change for MyDisposable.

What you can do, however, is to add an instance of this little class to the container:

 internal class Disposer : Component
{
    private Action<bool> dispose_;
 
    internal Disposer(Action<bool> disposeCallback)
    {
        this.dispose_ = disposeCallback;
    }
 
    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);
 
        this.dispose_(disposing);
    }
}

From your Form code, you'll need to wire up a new instance of Disposer with a delegate which will contain the real dispose logic. When used in MyForm, it now looks like this:

 public partial class MyForm : Form
{
    private MyDisposable myDisposable_;
 
    public MyForm()
    {
        InitializeComponent();
 
        this.myDisposable_ =
            new MyDisposable("Goodbye, World");
        this.components.Add(new Disposer(this.OnDispose));
    }
 
    private void OnDispose(bool disposing)
    {
        this.myDisposable_.Dispose();
    }
}

Notice that I have added a new Disposer instance that points back to the new OnDispose method, which then implements all the dispose logic not handled by the designer code. Now, when I run the application and close the Form, I get the expected MessageBox.

This technique is generally usable whenever you need to dispose of a member variable, but otherwise can't because the designer code already overrides the Dispose method. However, I only recommend this approach if your member variable doesn't also implement IComponent.

If the you have control over the member variable's class and don't mind having a default constructor, I recommend that you instead implement IComponent (the easiest way to do that is by deriving from System.ComponentModel.Component like I did with the Disposer class). This will generally give you a nicer design-time experience, since you can now just drag the component from the toolbox to the design surface, and the designer (Visual Studio) will automatically add it to the components member variable, and thus it will be disposed together with all the other components in the container.

Both approaches also work if you are creating a UserControl, although there, you'll probably need to instantiate the components member variable yourself.

Comments

  • Anonymous
    March 23, 2007
    Why not to use the Disposed-Event and dispose your own resources there? That seems to be a much more simple approach to me.

  • Anonymous
    March 23, 2007
    The comment has been removed

  • Anonymous
    May 03, 2007
    The comment has been removed

  • Anonymous
    May 03, 2007
    Hi Keith Thank you for your comment. You are absolutely right - I thought it was a good exercise for the alert reader ;)

  • Anonymous
    February 09, 2008
    The comment has been removed

  • Anonymous
    February 09, 2008
    The comment has been removed

  • Anonymous
    March 09, 2008
    Hi, I realise this is quite a while after your initial post... but I just came across this issue and found your blog. You say "You could obviously edit the designer file directly, but that's not recommended." Are you sure about this? The Dispose() method is outside the "Designer generated code" region in the source that VS produces, so it shouldn't be a problem to edit the method, or move it out of the designer file completely, right? Thanks for your help!

  • Anonymous
    March 09, 2008
    Hi Andy Thank you for your comment. Technically, I think you are correct, but as a rule of thumb, I prefer to leave the designer files alone. AFAIR, there's no documentation of VS that states that the VS designer only fiddles with the "Designer generated code" region, so you never know... Another reason I prefer to not edit the designer file is because it may throw off other team members. Let's say that you put some logic in the designer file and check it in. Half a year later, some other team member wastes a complete day troubleshooting some weird behavior because he or she doesn't think about looking in the designer file - it's not the first place you'll look for developer-created code :) So I admit that my motivation for not editing designer files is more 'soft' than entirely technical, but I still think they are good reasons. HTH

  • Anonymous
    June 10, 2008
    The comment has been removed

  • Anonymous
    June 10, 2008
    The comment has been removed

  • Anonymous
    June 11, 2008
    The comment has been removed

  • Anonymous
    June 11, 2008
    Hi Paul Thank you for your comment. Everything you are pointing out sounds correct to me, so I think your understanding of the subject is at least as good as mine :) The reason I think finalizers directly in custom Forms matter is this: Let's say that you create a custom form that (directly) holds an unmanaged resource. In this case, you should implement a finalizer in the form, so that the unmanaged resource can be freed even in the case where a client forgets to call Dispose. This finalizer code needs to be implemented somewhere, but you also want to clean up the unmanaged resource when disposing. Whereever you choose to implement this clean-up code, you need to know whether you are being called from Dispose or the finalizer. The callback for the Disposer's delegate (OnMyFormDisposed in the example above) is an obvious place to put this clean-up code. True: When initiated from the Form's Dispose method, OnMyFormDisposed will always be called with the 'disposing' parameter set to false, but you can still call it from the Form's own finalizer with the value of true. There are other solutions to this situation, but when available, I always like to go for one which feels familiar, and in the above way, I follow the normal Dispose pattern.

  • Anonymous
    September 08, 2008
    I agree with Andy, I think...  Although your method is clever you are adding a layer of complexity that is (in my opinion) unnecessary.  Like Andy I would simply remove the Dispose method from the designer file and place it in the main code file and finally modify this method. Just my opinion... Good blog and discussion!

  • Anonymous
    September 08, 2008
    Hi MAC To each his own :) I completely agree that my method adds another level of indirection (the standard solution to any OO problem), and whether you feel that it's worth the benefit is impossible to measure. I tend to work either by myself, or in tight collaboration with quite experienced developers, so for me, the added complexity comes at a relatively low price. If you work in a different development organization, the price may be much higher, to a degree where it makes more sense to directly edit the designer file. YMMV :)

  • Anonymous
    April 22, 2009
    A couple of questions: I ported the implementation to VB.NET for testing. What I want to achieve is having a place in form classes to dispose of unmanaged code (typical COM-objects) that has the IDisposable interface implemented. (as you)

  1. To instantiate the Disposer class, could this be put in the form Load event? (to have it outside the designer code)
  2. I previously have had a separate method to implement the dispose pattern (i.e. DisposeOfresources) which I call before Form.Dispose(). That is also a safe method or what?
  • Anonymous
    April 23, 2009
    Hi Lars Thank you for your questions.
  1. In my example, I instantiate the Disposer class in the Form's constructor, but I don't see why you couldn't instantiate it in OnLoad instead. The important step is to instantiate it before or simultaneously with your first unmanaged member. If you wait until after, someone may incorrectly use your Form and dispose of it prematurely, which would cause the Disposer to never get called.
  2. If I understand what you are saying, you are then placing the burden of remembering to call DisposeOfresources on the client of your API. E.g. if someone were to use the C# 'using' pattern with your Form, the DisposeOfresources method would not get called, so this doesn't sound safe.
  • Anonymous
    May 15, 2009
    great solution !!! though personally i would prefer the disposer to call dispose: public class MyDisposable : IDisposable    {        private string _message;        public MyDisposable(string message)        {            this._message = message;        }        #region IDisposable Members        public void Dispose()        {            MessageBox.Show(this._message);        }        #endregion    }    internal class Disposer : Component    {        private IDisposable _disposable;        internal Disposer(IDisposable disposable)        {            _disposable = disposable;        }        protected override void Dispose(bool disposing)        {            if (disposing)            {                if (_disposable != null)                {                    _disposable.Dispose();                }            }            base.Dispose(disposing);        }    }    public partial class Form1 : Form    {        private MyDisposable _myDisposable;        public Form1()        {            InitializeComponent();            _myDisposable = new MyDisposable("Goodbye, World");            components.Add(new Disposer(_myDisposable));        }

  • Anonymous
    June 22, 2009
    I'm coming very VERY late to this party, but one option that no-one seems to have proposed is taking the Dispose method OUT of the Designer file and sticking it in the .cs file. That way other programmers MAY be confused, but they WILL file it...

  • Anonymous
    April 07, 2011
    Nice one... I would agree that this is up to one's experience and method of writing but I would say that the code is neat and very easy to use so Thanks! for showing us a nice way to dispose.