Eran Kampf
Eran Kampf
5 min read

FinalizableObject - Developing a Base Class for IDisposable Objects

I’ve had to design a system that works with unmanaged resources and thus I had several classes that required the implementation of the IDisposable interface.

In order to enforce the same IDisposable implementation across the system I decided to implement a common base class that implements the IDisposable interface. Classes will derive from this base class rather than implementing IDisposable themselves. So, taking the IDisposable pattern implementation sample from the IDisposable sample at MSDN I have created the following base class:

public class FinalizableObject : IDisposable
{
    private bool _disposed = false;
    public FinalizableObject()
    {
    }

    #region IDisposable Members
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    #endregion

    protected void Dispose(bool disposing)
    {
        // Check to see if Dispose has already been called.
        if (!_disposed)
        {
            // If disposing equals true, dispose all managed and unmanaged resources.
            if (disposing)
            {
                DisposeManagedResources();
            }

            DisposeUnmanagedResources();
        }

        _disposed = true;
    }

    ~FinalizableObject()
    {
        Dispose(false);
    }

    protected virtual void DisposeManagedResources() {}
    protected virtual void DisposeUnmanagedResources() {}
}

The FinalizableObject class implements the IDisposable interface and allows derivative classes to override the DisposeManagedResources and DisposeUnmanagedResources methods and implement their cleanup code.

Why do we need these two methods? Well, disposal can occur explicitly (by calling the IDisposable.Dispose() method) or implicitly (from the Finalizer).

During explicit disposal we can take care of both managed and unmanaged resources used by the class. On the other hand, during the implicit disposal we can

only take care of unmanaged resources because the unamanged resources are not guerenteed to still exist.

Therefore, the disposal process had to be seperated to these two methods in order to handle both cases described above.

Finalization is Bad

Usually, when we take over a system resource (like a file etc.) we want to release it as soon as we’re done using it. Besides the fact that being in the finalization queue extends the life of the object, we do not want to wait on releasing our resource untill the GC decides to reclaim the object.

this is why our FinalizableObject will usually be disposed explicitly. If it reaches the Finalizer (implicit disposal) it is probably because a developer forgot to explicitly call the IDisposable.Dispose method.

So I decided to add the functionality for the FinalizableObject class to alert the developers if an object was not disposed explicitly as it was supposed to be.

Saving the StackTrace on the object’s creation and displaying it as an assertation error when the object is not explicitly disposed (when the Finalizer is called) should provide enough information to the developers in order to find out where the object was created and which section of code should be fixed.

So, after adding this modification, here’s the full code (and documentation) for the FinalizeableObject class:

///

/// Base class for classes implementing .
///
///
/// This class also implements a Finalizer which is used in case the Dispose method is not called by the user.
/// On DEBUG build an Assert exception is thrown with the stack trace information showing where the instance was created.
/// This class is not thread-safe (does not support calling Dispose() from multiple threads).
///
public class FinalizableObject : IDisposable
{
    #region Private Fields
#if DEBUG
    private System.Diagnostics.StackTrace _allocStack;
#endif //DEBUG
    private bool _disposed = false;
    #endregion

    ///

    /// Default constructor
    ///
    public FinalizableObject()
    {
#if DEBUG
        _allocStack = new System.Diagnostics.StackTrace();
#endif  //DEBUG
    }

    ///

    /// Finalization code.
    ///
    ~FinalizableObject()
    {
#if DEBUG
        System.Diagnostics.Debug.Assert(false, "FinalizableObject was not disposed" + _allocStack.ToString());
#endif //DEBUG
        Dispose(false);
    }

    #region IDisposable Members
    ///

    /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
    ///
    public void Dispose()
    {
        Dispose(true);
        // This object will be cleaned up by the Dispose method.
        // Therefore, you should call GC.SupressFinalize to
        // take this object off the finalization queue
        // and prevent finalization code for this object
        // from executing a second time.
        GC.SuppressFinalize(this);
    }

    ///

    /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
    ///
    /// True if called explicitly; otherwise (called by finalizer) False.
    ///
    /// Dispose(bool disposing) executes in two distinct scenarios. /// If disposing equals true, the method has been called directly
    /// or indirectly by a user's code. Managed and unmanaged resources
    /// can be disposed.
    /// If disposing equals false, the method has been called by the
    /// runtime from inside the finalizer and you should not reference
    /// other objects. Only unmanaged resources can be disposed.
    ///
    protected virtual void Dispose(bool disposing)
    {
        // Check to see if Dispose has already been called.
        if (!_disposed)
        {
            // If disposing equals true, dispose all managed
            // and unmanaged resources.
            if (disposing)
            {
                DisposeManagedResources();
            }

            DisposeUnmanagedResources();
        }

        _disposed = true;
    }
    #endregion

    protected virtual void DisposeManagedResources(){ /* Do some cleanup work here */ }
    protected virtual void DisposeUnmanagedResources(){/* Do some cleanup work here */}
}

Note that the stack trace is only captured when the DEBUG compilation flag is set. If the Finalizer code is reached the assertation displays the stack information so that the developer has enough information to track down the section in the code where the object was allocated and why it was not disposed properly.