Rules For Implementing IDisposable (Post 2 of 5)

C# Add comments

This is the second part of a series of five posts. Here is the first post in this series: Deterministic Finalization in Garbage-Collected Languages. I have adjusted the entry date/time to make the series sequential.

In my last post, Deterministic Finalization in Garbage-Collected Languages (e.g. C#’s IDisposable/using), I discussed what IDisposable and using are and why they are useful. This post describes some rules when implementing IDisposable in a class:

  • Follow the .NET Framework Developer’s Guide Implementing a Dispose Method guidelines.
  • Try hard not to throw an Exception from Dispose() (this is analagous to the C++ basic exception safety guarantee’s demand that destructors not throw exceptions). One major reason why you should avoid throwing an Exception from Dispose() is that it could mask any Exception that is “currently” being thrown.
  • If you write a class which contains a member which implements IDisposable, your class must implement the IDisposable interface and dispose the member in its Dispose() method. (This rule obviously can become very annoying as IDisposable propagates up your implementation hierarchy.) For example:

    // FileStreamHolder must implement IDisposable because it contains
    // a member which implements IDisposable (FileStream).
    class FileStreamHolder : IDisposable
    {
        private FileStream m_fileStream;
    
        public void Dispose()
        {
            Dispose(true);
        }
    
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                // We only dispose IDisposable members if we reached this
                // function through IDisposable.Dispose().
                m_fileStream.Dispose();
            }
        }
    }
    
  • It can be useful to add a debug-only finalizer which verifies that Dispose() was called for your object. For example:
    class DisposableObject : IDisposable
    {
    #if DEBUG
        ~DisposableObject()
        {
            // The finalizer should never be called -- Dispose() should have
            // been called instead.
            Debug.Assert(false, "This object was not disposed using Dispose().");
        }
    #endif
    
        public void Dispose()
        {
            Dispose(true);
    #if DEBUG
            // The finalizer only exists in debug builds
            GC.SuppressFinalize(this);
    #endif
        }
    
        protected virtual void Dispose(bool disposing)
        {
            ....
        }
    }
    

    Incidentally, I use #if DEBUG instead of the [Conditional("DEBUG")] attribute because Conditional is not allowed on finalizers.

  • Beware of classes which implement Dispose() only through the IDisposable interface and not through the general interface of the class, as IntelliSense may not show the Dispose() method for the class, and calling Dispose() directly won’t compile. (Example: TextWriter) Use ((IDisposable) obj).Dispose(); instead.

In my upcoming posts I will show some useful classes I’ve written which implement IDisposable.

Update 2006-03-28 12:38 PM: See More on IDisposable for some further discussion.

Comments are closed.

WP Theme & Icons by N.Design Studio
Entries RSS Comments RSS Log in