次の方法で共有


A Little Trick to Ensure that Dispose is Called

Typically trying to avoid finalizers, there is one place* where they come in handy. That place is making sure that all consumers of your objects call Dispose either explicitly or using the using construct. The trick is so simple -- there is no rocket science or otherwise involved.

Basically, the idea is that you in your class you have a Finalizer and the Dispose method. In your Dispose method, you do what you would normally do and you call GC.SuppressFinalize(this). All of this happens within a #if DEBUG ... #endif.

#region IDisposable Members

  public void Dispose()

  {

    ...

#if DEBUG

  GC.SuppressFinalize(this);

#endif

    ...

  }

#endregion 

 Obviously, you can have more statements and instructions in your Dispose. Your finalizer will look something like this:

#if DEBUG

  ~DisposableClass()

  {

  Debug.Assert(false, "Disposable object not disposed");

  }

#endif

Of course, the magic that makes this work is that if Dispose is ever invoked, the finalizer is never invoked. Conversely, if the finalizer is invoked, then Dispose was not. This is a great way to ensure that your disposable objects are actively disposed of and if they are not, then you are made aware of that fact with a loud fanfare.

I am imagining that you could probably do some more fancy tracking by recording the stack frame of the allocation and display that when your finalizer asserts false. I will leave this as an exercise for the reader**.

* There may be others, but with SafeHandle I can't think of many places where you would need to keep explicit track of unmanaged resources.

** Through all my years in university, I always hated when professors would leave interesting points as exercises left to the reader. If you are trying to drive a point, make sure that it is not left to chance if you get it across or not. However, I have waited many years to use that same sentence, so here it is.

Comments

  • Anonymous
    December 28, 2008
    The so-called dispose-pattern revisited? :-)

  • Anonymous
    January 13, 2009
    This trick is nice, but not practicable for me. I have a class with an unmanaged window pointer inside. The window closes when the class is disposed. But when the dispose is forgotten, the window stays open until the finalizer does its job! I want to strictly forbit a finalization. Isn't there any way to force the creation of a class on stack only ?

  • Anonymous
    January 14, 2009
    You can't create classes (reference types) on the stack. You can create structs which will cause the object to be created on the stack. The issue here, though, is that if you move the object from place to place, a new one gets created for you. The effect of this is that Dispose will be called prematurely. Probably not what you want, right?

  • Anonymous
    January 14, 2009
    Yes, this is the right way, but not enough. I cannot forbid our users to create this structs with gcnew. Then the user HAS to call the dispose method to make the window in the struct disappear. But what if the dispose method is forgotten ? Then the window is visible until the garbagecollector does its job.