So first off this is not new, it’s been around for a long, long while. One issue I have with IDisposable is that for whatever reason .Net did not include a base class for handling the details… I admit you may not always be able to leverage it since you may have a base class, yet for me that seems a rare case. So taking time to fix a few issues in this recommendation, I finally rolled this base class: CSharpTest.Net.Bases.Disposable.

I thought I’d explain some deviations from some normal practices in this implementation:

1. – Finalizers should not throw… ever. It does bad things to the current GC cleanup.

	~Disposable()
	{ try { OnDispose(false); } catch { } }

2. – If the Dispose implementation throws in a call to Dispose there is no need to try again on Finalize.

	public void Dispose()
	{
		try { OnDispose(true); }
		finally { GC.SuppressFinalize(this); }
	}

3. – How does a derived class know if it’s been disposed and should throw an ObjectDisposedException? It should be easy, call this.Assert():

	protected virtual void Assert()
	{
		if (_isDisposed) throw new ObjectDisposedException(GetType().FullName);
	}

4. – What about knowing when an object is disposed? This is often a necessity, or you may simply what to chain a few more objects to be disposed along with this one. IMHO, The Disposed event might even have been part of IDisposable (or maybe a derived interface). Having this event greatly reduces the need for things like DisposingList<T> and the DisposingStream.

	private event EventHandler DisposedEvent;
	public event EventHandler Disposed
	{
		add { Assert(); DisposedEvent += value; }
		remove { DisposedEvent -= value; }
	}

5. – Lastly we just need to ensure the Dispose happens correctly and fire the event. You might notice I did not ensure the event would fire. The reason is simple, say Dispose(bool) throws and then a finally or catch block fires the event… and that event throws a new exception. There is not a really nice way to handle something like this, perhaps the dispose of this caused the event’s handler to fail? I felt the best answer was to propagate the first exception encountered and by doing that I prevent masking errors. Regardless of the outcome we will not try again, we are disposed and the event is cleared.

	private bool _isDisposed = false;
	private void OnDispose(bool disposing)
	{
		try
		{
			if (!_isDisposed)
			{
				Dispose(disposing);
				if (DisposedEvent != null)
					DisposedEvent(this, EventArgs.Empty);
			}
		}
		finally
		{
			_isDisposed = true;
			DisposedEvent = null;
		}
	}
Comments