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;
		}
	}
 

This one is actually something of a triviality, yet it can be very useful at times. Now a word of caution is due here, this object is built on System.Threading.Timer and should not be used to enqueue 12 million work items. Yet if you need to perform a simple task at some time in the future the “TimeoutAction” class can serve nicely. The follow example uses it to clean up a temp file after giving a spawned process time enough to open the file:

using CSharpTest.Net.Delegates;
using CSharpTest.Net.IO;

    public interface IDownloadedFile
    {
        string Extension { get; }
        void CopyTo(string filename);
    }
    public void SpawnDownload(IDownloadedFile file)
    {
        using (TempFile tmpFile = TempFile.FromExtension(file.Extension))
        {
            file.CopyTo(tmpFile.TempPath);
            System.Diagnostics.Process.Start(tmpFile.TempPath);
            //Schedule the file cleanup to happen after one minute.
            TimeoutAction.Start(TimeSpan.FromMinutes(1), TempFile.Delete, tmpFile.Detatch());
        }
    }

This beats the heck out of a work queue or writing a thread for simple and infrequent operations. One additional concern addressed, if the object is disposed or GC’d the event will fire giving you the opportunity to perform the task. In addition to example above, it is also possible to provide an Action to handle any errors, by default it writes to Diagnostics.Trace.

 

Changes in this version:

  • Added CmdTool.exe – the last Visual Studio code generator you’ll ever need :)
    • Code generation made easy, just write a command line tool.
    • No shutting down Visual Studio when you change your code generation tool.
    • Integrates with Visual Studio 2005, 2008, or 2010.
    • Displays console output in Visual Studio’s output window.
    • Clean or Build generated content directly from a command-line.
    • Self-registering, simply run: CmdTool.exe register
    • Read the sample configuration file for more information.
  • Added CSharpTest.Net.Bases.Disposable – a default base class implementing IDisposable with a Disposed event.
  • Added CSharpTest.Net.Crypto.HashStream – for creating a hash value by simply writing values to a stream.
  • Added CSharpTest.Net.Delegates.TimeoutAction – for executing a delegate after a fixed period of time.
  • Added CSharpTest.Net.IO.TempDirectory – same as TempFile but creates/deletes an entire directory.
  • Added CSharpTest.Net.Processes.ScriptRunner – for executing and capturing the output of various scripts.
  • Added CSharpTest.Net.Synchronization namespace – Provides common interfaces to reader/writer and exclusive locking.
  • CSharpTest.Net.Delegates.EventHandlerForControl – Fix for COM hosted controls – TopLevelControl returns null.
  • CSharpTest.Net.Html.IXmlLightReader – Breaking change – extended Start/End tag with structured argument.
  • CSharpTest.Net.Html.XmlLightElement – Now has the ability to recreate the input via WriteUnformatted(TextWriter).
  • CSharpTest.Net.Processes.ProcessRunner – Fixed some issues with IsRunning on STA threads, and fixed Kill().
  • CSharpTest.Net.Reflection.PropertyType – Now exposes attributes defined on the member reflected.
  • Build.bat – Default framework is now 3.5, see CSBuild.exe.config to change build to 2.0.