6/24/2005

When do I need to implement a finalizer or IDisposable?

Recently another developer was experimenting with structs we are using to serialize a class for eventual mapping into an XmlDocument to transmit to a vendor. I noticed he had derived from IDisposable but had not implemented any Dispose pattern for the struct. I queried him on it, "What is the rationale for implementing IDisposable on a struct (a value type)?" (I just love to challenge other developers, even when I haven't a clue what the hell I'm talking about).

Fact of the matter is, this is one of the most esoteric areas of working with the .NET Framework, and if you search around, you will find a lot of people simply parroting what they have seen elsewhere, which of course leads you on a wild goose chase to nowhere pretty fast.

The Nitty-Gritty: Finalizers only need to be implemented when you hold onto resources that need cleaning up.

Example:
FileStream holds onto a native file handle and implements a finalizer to release the handle when the FileStream is garbage collected. Finalizers place a lot of pressure on the garbage collector and should be used only when absolutely needed.

Dispose generally goes with Finalizers. The IDisposable interface tells the world that your class holds onto resources that need to be disposed and provides users a way to release them. Any class that implements a finalizer should also implement IDisposable. But, not all classes that implement IDisposable need to or even should implement a finalizer. Look at the various classes that actually have a Dispose method to get more insight (Ex. SqlConnection).

Let's say a managed class has a FileStream as a private member. FileStream holds onto unmanaged resources and implements both IDisposable and a finalizer. When no more references to the instance exist, the FileStream will be unreachable and will be available for finalization. There's no reason for the class having FileStream as a member to be in the queue of objects that have registered for finalization since the instance of FileStream will be there. But the class should provide a way for a user to immediately release all resources it holds onto, either directly or indirectly, and so the class should implement IDisposable. Your implementation of Dispose will simply call the FileStream's Dispose method. Be careful not to dispose of shared resources (resources used by other instances, for example).

If you do need to implement a finalizer in your class, your Dispose method should use the GC.SuppressFinalize method to ensure that finalization of your instance is suppressed. This will remove the instance from the set of objects that require finalization, reducing the pressure on the garbage collector during a collection. A common pattern implemented throughout the Microsoft® .NET Framework is to add to a class a Dispose method that takes a Boolean as a parameter. This Boolean indicates whether the class is being disposed because the IDisposable.Dispose method is called or because the finalizer is run (both the finalizer and IDisposable.Dispose delegate to this method). If it's being disposed deterministically, GC.SuppressFinalize is invoked. If it's being called from a finalizer, avoid using managed members of your class that implement finalizers as they may have already been finalized.

The Patterns and Practices Book provides an excellent code example of how to implement an IDisposable Pattern in your code.

The following table provides guidelines on when to implement these constructs in your classes:





Your Class Has IDisposableFinalizer
Only managed resources that don't implement IDisposable or have any way of being closed
Only managed resources, but some of them implement IDisposable or can be closed in some wayX
Both managed resources and unmanaged resources XX
Only unmanaged resources XX






Short answer is, you will be hard - pressed to find a good reason to implement IDisposable on a struct! IDisposable is not some magical chicanery behind the scenes. It's simply an Interface that allows you to provide a standardized way for callers to tell your class they want it to go away and clean up it's room before it leaves. If it doesn't have a lot of junk in it's room to clean up, you can just close the door instead and everything will be fine. Rest assured, their room will disappear in the next mudslide. A more detailed treatment of this issue, including a revealing "gotcha" about the SqlConnection class and Close vs. Dispose, can be found in this article.