For ideas on when to implement IDisposable, see another of my blog posts, Implement IDisposable More.

If you decide that your class should implement IDisposable, you have done your part to be sure that resources can be cleaned up immediately with a call you your Dispose method.

However, what if another object fails to call your Dispose method? Your Dispose method will never run.

In most cases, if a Dispose method does not run, I consider it a coding error. To catch it, I developed this simple enforcement trick:

public class SomeDisposableClass: IDisposable
{
bool isDisposed;
StreamReader myStreamReader;

public SomeDisposableClass()
{
myStreamReader = new StreamReader(“c:\foo.txt”);
}

public void Dispose()
{
isDisposed = true;

if (myStreamReader != null)
myStreamReader.Dispose();
}

#if DEBUG
~SomeDisposableClass()
{
if (!isDisposed)
Console.WriteLine(“An instance of SomeDisposableClass was not disposed before garbage collection.”);
}
#endif
}

A private field called isDisposed defaults to false, but is set to true if the Dispose method runs. If the Dispose method does not run, a finalizer catches the coding error by outputting a message to the console window.

Objects with finalizers pay a garbage collection performance penalty. To avoid that penalty, the above code only compiles in the finalizer in debug mode.

Typically, you can spot these error messages in the console window when garbage collection runs at application shut-down. However, they can show up at any time if your object is garbage collected sooner.

If you really want to enforce that your Dispose methods get called, throw an exception instead of outputting a message to the console window.

Be careful what you do in your finalizer. The finalizer will run on a garbage-collection thread, so avoid the temptation to reference any non-thread-safe fields. While it would be nice to pop up a message box to report not-disposed errors, during application shutdown, you may or may not catch a glimpse of it before shutdown completes.

Please add comments to refine the use of IDisposable or to show support for this enforcement trick.

Enjoy,

Dale

Technorati Tags: ,

The following code snippet demonstrates the only way that I know of that the C# language specification treats IDisposable differently than any other interface:

// StreamReader implements IDisposable.
using (var myStreamReader = new StreamReader(“c:\foo.txt”))
{
Console.WriteLine(myStreamReader.ReadLine());
}

StreamReader implements the IDisposable interface and can therefore be used with the using C# keyword. At the end of the using block, the myStreamReader object’s Dispose() method will run automatically.

You may speculate that an object implementing IDisposable somehow pays a penalty, perhaps by demotion to a slower garbage-collection tier, but according to Microsoft, IDisposable works like any other interface in garbage collection.

Although C# does not penalize objects implementing IDisposable, the usual recommendation to let the common language runtime (CLR) garbage-collect them without worrying about calling Dispose methods makes good sense.

I read somewhere that you should implement IDisposable only if your class references unmanaged resources. I think implementing IDisposable can improve quality in several ways:

  1. For performance reasons, you wish to free a resource sooner rather than later. For example, while a managed database connection or file handle will close when the garbage collector collects the object, you may wish to free the resources as soon as your application no longer needs them.
  2. You need to call the Dispose method on sub-objects that you own.
  3. Ease verifying correctness when working with complex object relationships. If, in a Dispose method, you clear values for all or most of your fields, then if some other object is mistakenly continuing to reference a field on the disposed object, you will likely catch the error quickly as a null reference or out-of-range error. I have found this to be especially helpful when forgetting to unregister from an event. When the event handler runs, it usually references a cleared out field and breaks, thus alerting me to my coding error.
  4. And of course, if you have unmanaged resources, you should implement IDisposable to close/free/release them.
  5. ?
  6. ?
  7. ?

Do you use IDisposable for reasons not mentioned above? Please add comments to describe those cases.

Please add comments to help refine the use of IDisposable or to show your support of this post.

Thanks,

Dale

Technorati Tags: ,