Dispose Dos and Don’ts

Due to the positive response on my previous entry on Dispose, I thought I’d write another, this time on what one should and shouldn’t do with a Dispose method.

  • Dispose should contain code to release any unmanaged resources associated with the object’s instance. For example, any handles should be released, database connections closed, etc. This simulates deterministic finalization by letting the developer do the resource cleanup when he/she is done with the object. You may want to use an IsDisposed bool and check it inside Dispose.
  • Dispose should be safely callable multiple times. In my example above, if some user of your class called Dispose twice, we would crash with an unhandled InvalidOperationException trying to โ€œdouble freeโ€ the GCHandle.
  • If you inherit from an IDisposable object, call the base class’ Dispose method. Unlike finalizers, which the GC calls, Dispose is only called by user code (sometimes automatically in C#, as I covered previously). This means any unmanaged resources held by your object’s parent, must also be disposed of.
  • Suppress finalization in Dispose. There has been some debate about whether GC.SuppressFinalization(this) should go before or after the cleanup code. Some argue after, in case something goes wrong before you’ve finished cleaning up. Others argue before, otherwise you may open up a race condition between your finalizer and your Dispose method. You should consider both strategies depending on your object’s implementation.
  • Don’t throw exceptions in Dispose. Nothing should go wrong with your object calling Dispose. Mainly for reasons stated above.
  • Throw ObjectDisposedException if a caller tries to use a released resource. You may be in a situation where you’ve disposed of your object because you no longer need the unmanaged resource, but you still need to have the object hanging around. If this is the case, any methods that would normally access the resource should throw an ObjectDisposedException, while other (possibly useful) methods should not.
  • Don’t call Dispose from one thread, while still using the object on another. Try to make the thread that created the object be the one that disposes of it. This is more general good multithreaded programming advice, then Dispose specific.
  • Whenever possible, follow the Dispose Pattern. That link, one more time: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconfinalizedispose.asp.

If you have any other Dispose Do’s and Don’ts, please post them in the comments.

Comments (19)

  1. In terms of ObjectDisposedException I’d argue that you should always throw it after a client has called Dispose (other than recalling the Dispose method). They don’t know which methods access a resource you release and which don’t (unless they have an uhealthy knowledge of your implementation).

    Dispose to me means "I’m finished with you". So in that case they have no business calling your methods beyond that point.

  2. Chris Lyon says:

    Hi Richard

    You could imagine a class that has a property like IsClosed, or a static method that you wouldn’t want to throw. Also consider inherited methods (in particular methods that inherit from System.Object, like ToString()).

    I hesitate to lay down "always do it this way…" rules, because there are always exceptions.

  3. How could you end up in a race condition between the finalizer and dispose? If dispose is running, someone needs to have called it – presumable from a live root. So unless the GC has made a catastrophic error and decided you were garbage when you had a live root; or another object called your Dispose in their Finalizer (which they definitely shouldn’t) – how is the Finalizer going to be running at the same time as Dispose?

  4. OK – granted about statics – I wouldn’t consider statics anyway as they are not involved with the instance being Disposed. But I guess the IsClosed is valid. I suppose I prefer to lay down an absolute and then let people argue why their situation is different ๐Ÿ˜‰

  5. Chris Lyon says:

    Richard, please read the comments from my previous blog entry and the links to Chris Brumme’s blog for an explanation on this race condition.

  6. Chris Lyon says:

    Richard, you probably don’t want to throw inside overloaded operators or methods inherited from interfaces, just to name a few more examples.

  7. Ugghh – Resurrection

    Is there actually a useful purpose for resurrection? I have to say its always looked like an abomination to me and so never really been that bright on my radar.

    I find it slightly perverse to prefer guarding against someone resurrecting my object than against protecting against clean up failure in Dispose. To me the latter is a much more likely scenario and anyone resurrecting my object deserves everything they get ๐Ÿ˜‰

  8. Chris Lyon says:

    It’s clear to me now that I need to dedicate a whole blog entry on resurrection ๐Ÿ™‚

    Unless resurrection is an expected scenario, guarding against it isn’t really necessary (most BCL classes don’t).

  9. Chris, I can kind of see why throwing in an operator overload would be bizarre for a consumer, but why do you say an interface implementation? What if that implementation accesses the released resource?

  10. Chris Lyon says:

    Interfaces generally specify behaviour, not implementation, so the Interface is often seperate from the resource.

    For example, IClomparable.CompareTo shouldn’t necessarily throw if the object has been disposed of.

    Again, these are guidelines, not hard rules.

  11. Agreed that interface define behaviour, but the issue is whether the implementation can satisfy that behaviour once Dispose has been called. But as you say – they are only guidelines (have you seen Pirates of the Caribbean? ๐Ÿ˜‰ )

    I’m looking forward to your blog post on resurrection – it will be interesting to here about the BCL classes that have been built with it in mind.

  12. Describes a base class for disposable objects.

  13. Describes a base class for disposable objects.

  14. Describes a base class for disposable objects.

  15. dcjeff says:

    No sure I agree with "Don’t throw exceptions in Dispose".  There are certainly places in the .net framework where this happens (e.g. disposing a faulted wcf client).  I also haven’t been able to find anything in the official documentation warning against this.