IDisposable and Class Hierarchies

In my previous post, I showed how the Dispose Pattern is effectively obsolete. But, there’s one area that I didn’t really cover.  What do you do when you want to create a class that implements IDisposable, doesn’t implement the Dispose Pattern, and will be derived from classes that will also implement disposal?

The Dispose Pattern covered this by coincidence.  Since something that derives from a class that implements the Dispose Pattern simply overrides the Dispose(bool) method, you effectively have a way to chain disposal from the sub to the base. There’s a lot of unrelated chaff that comes along with Dispose Pattern if that’s all you need.  What if you want to design a base class that implements IDisposable and support sub classes that might want to dispose of managed resources?  Well, you’re not screwed.

You can simply make your IDisposable.Dispose method virtual and a sub can override it before calling the base.  For example:

	public class Base : IDisposable
	{
		private IDisposable managedResource;
		//...
		virtual public void Dispose()
		{
			if(managedResource != null) managedResource.Dispose();
		}
	}
 
	public class Sub : Base
	{
		private IDisposable managedResource;
		public override void Dispose()
		{
			if (managedResource != null) managedResource.Dispose();
			base.Dispose();
		}
	}

If you don’t implement a virtual Dispose and you don’t implement the Dispose Pattern, you should use the sealed modifier on your class because you’ve effectively made it impossible for base class to dispose of both their resources and the base’s resources in all circumstances.  In the case of a variable declared as the base class type that holds an instance of a subclassed type (e.g. Base base = new Sub()) only the base Dispose will get invoked (all other cases, the sub Dispose will get called).

Caveat

If you do have a base class that implements IDisposable and doesn’t implement a virtual Dispose or implement the Dispose Pattern (e.g. outside of your control) then you’re basically screwed in terms of inheritance.  In this case, I would prefer composition over inheritance.  The type that would have been the base simply becomes a member of the new class and is treated just like any other disposable member (dealt with in the IDisposable.Dispose implementation).  For example:

	public class Base : IDisposable
	{
		//...
		public void Dispose()
		{
			//...
		}
	}
 
	public class Sub : IDisposable
	{
		private Base theBase;
		//...
 
		public void Dispose()
		{
			theBase.Dispose();
		}
	}

This, of course, means you need to either mirror the interface that the previously-base-class provides, or provide a sub-set of wrapped functionality so the composed object can be used in the same ways it could have been had it been a base class.

This is why it’s important to design consciously—you need to understand the ramifications and side-effects of certain design choices.


3 thoughts on “IDisposable and Class Hierarchies”

  1. “Dispose Pattern” is still valid and necessary. The “Dispose()” of the base class could include some instructions that must be executed. If you override the “Dispose()” you are removing those instructions.

    In your example the developer must know that “Base” class has a private resource to remove from memory.

    I recommend this book to learn these kind of patterns:

    Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries

    http://www.amazon.com/Framework-Design-Guidelines-Conventions-Libraries/dp/0321545613

    It is written by the developers of .NET framework.

  2. @Miguel If you override an existing Dispose method, you must assume that that method had a purpose. To override it and ignore it assumes a lack of skill and expertise that one could only assume means would result in an incorrect implementation of the Dispose Pattern too. In the very same book you reference includes rules like “AVOID making types finalizable” and “DO make a type finalizable, *if the type is responsible for releasing an unmanaged resource that does not have it’s own finalizer*”. Implementing the Dispose Pattern requires you make a type finalizable. With the type SafeHandle, there is almost never a reason to manually implement a finalizer to release unamanaged resources (i.e. all classes that deal with unmanaged resources have a Dispose method). With hardly ever a reason to implement a finalizer, there’s really no reason to implement the Disposable Pattern in non-framework code.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>