Dispose pattern - DisposableObject I am trying to make an universal implementation of IDisposable (as a base class):

public abstract class DisposableObject : IDisposable
{
    private bool hasUnmanagedResources;
    private bool baseDisposeManagedResourcesCalled;
    private bool baseDisposeUnmanagedResourcesCalled;

    protected bool IsDisposed { get; private set; }
    protected bool IsDisposing { get; private set; }

    public DisposableObject()
        : this(false)
    {
    }

    public DisposableObject(bool hasUnmanagedResources)
    {
        this.hasUnmanagedResources = hasUnmanagedResources;

        if (!hasUnmanagedResources)
        {
            GC.SuppressFinalize(this);
        }
    }

    ~DisposableObject()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        if (hasUnmanagedResources)
        {
            GC.SuppressFinalize(this);
        }
    }

    protected virtual void DisposeManagedResources()
    {
        if (!IsDisposing)
        {
            throw new InvalidOperationException(
                "In order to dispose an object call the Dispose method. Do not call DisposeManagedResources method directly." +
                    GetTypeString());
        }

        baseDisposeManagedResourcesCalled = true;
    }

    protected virtual void DisposeUnmanagedResources()
    {
        if (!IsDisposing)
        {
            throw new InvalidOperationException(
                "In order to dispose an object call the Dispose method. Do not call DisposeUnmanagedResources method directly." +
                    GetTypeString());
        }

        baseDisposeUnmanagedResourcesCalled = true;
    }

    protected void HasUnmanagedResources()
    {
        if (!hasUnmanagedResources)
        {
            hasUnmanagedResources = true;
            GC.ReRegisterForFinalize(this);
        }
    }

    protected void ThrowIfDisposed()
    {
        if (IsDisposed)
        {
            throw new ObjectDisposedException("Object is already disposed." + GetTypeString());
        }

        if (IsDisposing)
        {
            throw new ObjectDisposedException("Object is being disposed." + GetTypeString());
        }
    }

    private void Dispose(bool disposeManagedResources)
    {
        if (IsDisposed)
        {
            throw new InvalidOperationException("Dispose called on an object that is already disposed." + GetTypeString());
        }

        if (IsDisposing)
        {
            throw new InvalidOperationException("Dispose called on an object that is currently disposing." + GetTypeString());
        }

        IsDisposing = true;

        if (disposeManagedResources)
        {
            DisposeManagedResources();
            VerifyBaseDisposeManagedResourcesCalled();
        }

        if (hasUnmanagedResources)
        {
            DisposeUnmanagedResources();
            VerifyBaseDisposeUnmanagedResourcesCalled();
        }

        IsDisposed = true;
        IsDisposing = false;
    }

    private void VerifyBaseDisposeManagedResourcesCalled()
    {
        if (!baseDisposeManagedResourcesCalled)
        {
            throw new InvalidOperationException("DisposeManagedResources of the base class was not called." + GetTypeString());
        }
    }

    private void VerifyBaseDisposeUnmanagedResourcesCalled()
    {
        if (!baseDisposeUnmanagedResourcesCalled)
        {
            throw new InvalidOperationException("DisposeUnmanagedResources of the base class was not called." + GetTypeString());
        }
    }

    private string GetTypeString()
    {
        string typeName = GetType().FullName;
        return " Type: " + typeName;
    }
}


By default, the object is removed from the finalization queue (in the DisposableObject constructor). If some inheriting class has an unmanaged resource then it should invoke HasUnmanagedResources method to add the object back to the finalization queue.

The most important question is: can I invoke GC.SuppressFinalize(this) in the constructor?.

How do you find this implementation?
Firstly, to address inheritance concerns, I use a t4 template to get around the single inheritance problem. ie. for every class where I need to introduce dispose support, I generate the an abstract base class that inherits from that class. Works a treat.

Now, to your code. Here are the things that jump out at me:


Constructors should be protected because the base class is abstract. This would make the code clearer to me, but YMMV.
Lose the whole unmanaged resource tracking. It adds complexity without value. Just call SuppressFinalize() regardless - it's the recommended pattern, anyway.
Your code is not thread-safe. Consider, for example, what happens if multiple threads call Dispose() simultaneously. You're got the potential for multiple calls to DisposeManagedResources() etcetera.


FWIW, here is an example Disposable base class generated by my t4 template:

// Generated by t4 template in Utility project. DO NOT EDIT!
#pragma warning disable 1591, 0612
#region Designer generated code

namespace Foo
{
    using System;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.Threading;
    using Kent.Boogaart.HelperTrinity.Extensions;

    /// &lt;summary&gt;
    /// A useful base class for disposable objects that inherit from &lt;see cref="object"/&gt;.
    /// &lt;/summary&gt;
    /// &lt;remarks&gt;
    /// &lt;para&gt;
    /// This base class simplifies the implementation of disposable objects by providing common functionality such as thread-safety on &lt;see cref="Dispose"/&gt; calls,
    /// and a &lt;see cref="Disposing"/&gt; event for pre-disposal notification.
    /// &lt;/para&gt;
    /// &lt;/remarks&gt;
    public abstract class DisposableBase : object, IDisposable
    {
        private const int DisposalNotStarted = 0;
        private const int DisposalStarted = 1;
        private const int DisposalComplete = 2;

#if DEBUG
        // very useful diagnostics when a failure to dispose is detected
        private readonly StackTrace creationStackTrace;
#endif

        // see the constants defined above for valid values
        private int disposeStage;

#if DEBUG
        /// &lt;summary&gt;
        /// Initializes a new instance of the DisposableBase class.
        /// &lt;/summary&gt;
        protected DisposableBase()
        {
            this.creationStackTrace = new StackTrace(1, true);
        }

        /// &lt;summary&gt;
        /// Finalizes an instance of the DisposableBase class.
        /// &lt;/summary&gt;
        [SuppressMessage("Microsoft.Design", "CA1063", Justification = "The enforced behavior of CA1063 is not thread-safe or full-featured enough for our purposes here.")]
        ~DisposableBase()
        {
            var message = string.Format(CultureInfo.InvariantCulture, "Failed to proactively dispose of object, so it is being finalized: {0}.{1}Object creation stack trace:{1}{2}", this.ObjectName, Environment.NewLine, this.creationStackTrace);
            Debug.Assert(false, message);
            this.Dispose(false);
        }
#endif

        /// &lt;summary&gt;
        /// Occurs when this object is about to be disposed.
        /// &lt;/summary&gt;
        public event EventHandler Disposing;

        /// &lt;summary&gt;
        /// Gets a value indicating whether this object is in the process of disposing.
        /// &lt;/summary&gt;
        protected bool IsDisposing
        {
            get { return Interlocked.CompareExchange(ref this.disposeStage, DisposalStarted, DisposalStarted) == DisposalStarted; }
        }

        /// &lt;summary&gt;
        /// Gets a value indicating whether this object has been disposed.
        /// &lt;/summary&gt;
        protected bool IsDisposed
        {
            get { return Interlocked.CompareExchange(ref this.disposeStage, DisposalComplete, DisposalComplete) == DisposalComplete; }
        }

        /// &lt;summary&gt;
        /// Gets a value indicating whether this object has been disposed or is in the process of being disposed.
        /// &lt;/summary&gt;
        protected bool IsDisposedOrDisposing
        {
            get { return Interlocked.CompareExchange(ref this.disposeStage, DisposalNotStarted, DisposalNotStarted) != DisposalNotStarted; }
        }

        /// &lt;summary&gt;
        /// Gets the object name, for use in any &lt;see cref="ObjectDisposedException"/&gt; thrown by this object.
        /// &lt;/summary&gt;
        /// &lt;remarks&gt;
        /// Subclasses can override this property if they would like more control over the object name appearing in any &lt;see cref="ObjectDisposedException"/&gt;
        /// thrown by this &lt;c&gt;DisposableBase&lt;/c&gt;. This can be particularly useful in debugging and diagnostic scenarios.
        /// &lt;/remarks&gt;
        /// &lt;returns&gt;
        /// The object name, which defaults to the class name.
        /// &lt;/returns&gt;
        protected virtual string ObjectName
        {
            get { return this.GetType().FullName; }
        }

        /// &lt;summary&gt;
        /// Disposes of this object, if it hasn't already been disposed.
        /// &lt;/summary&gt;
        [SuppressMessage("Microsoft.Design", "CA1063", Justification = "The enforced behavior of CA1063 is not thread-safe or full-featured enough for our purposes here.")]
        [SuppressMessage("Microsoft.Usage", "CA1816", Justification = "GC.SuppressFinalize is called indirectly.")]
        public void Dispose()
        {
            if (Interlocked.CompareExchange(ref this.disposeStage, DisposalStarted, DisposalNotStarted) != DisposalNotStarted)
            {
                return;
            }

            this.OnDisposing();
            this.Disposing = null;

            this.Dispose(true);
            this.MarkAsDisposed();
        }

        /// &lt;summary&gt;
        /// Verifies that this object is not in the process of disposing, throwing an exception if it is.
        /// &lt;/summary&gt;
        protected void VerifyNotDisposing()
        {
            if (this.IsDisposing)
            {
                throw new ObjectDisposedException(this.ObjectName);
            }
        }

        /// &lt;summary&gt;
        /// Verifies that this object has not been disposed, throwing an exception if it is.
        /// &lt;/summary&gt;
        protected void VerifyNotDisposed()
        {
            if (this.IsDisposed)
            {
                throw new ObjectDisposedException(this.ObjectName);
            }
        }

        /// &lt;summary&gt;
        /// Verifies that this object is not being disposed or has been disposed, throwing an exception if either of these are true.
        /// &lt;/summary&gt;
        protected void VerifyNotDisposedOrDisposing()
        {
            if (this.IsDisposedOrDisposing)
            {
                throw new ObjectDisposedException(this.ObjectName);
            }
        }

        /// &lt;summary&gt;
        /// Allows subclasses to provide dispose logic.
        /// &lt;/summary&gt;
        /// &lt;param name="disposing"&gt;
        /// Whether the method is being called in response to disposal, or finalization.
        /// &lt;/param&gt;
        protected virtual void Dispose(bool disposing)
        {
        }

        /// &lt;summary&gt;
        /// Raises the &lt;see cref="Disposing"/&gt; event.
        /// &lt;/summary&gt;
        protected virtual void OnDisposing()
        {
            this.Disposing.Raise(this);
        }

        /// &lt;summary&gt;
        /// Marks this object as disposed without running any other dispose logic.
        /// &lt;/summary&gt;
        /// &lt;remarks&gt;
        /// Use this method with caution. It is helpful when you have an object that can be disposed in multiple fashions, such as through a &lt;c&gt;CloseAsync&lt;/c&gt; method.
        /// &lt;/remarks&gt;
        [SuppressMessage("Microsoft.Usage", "CA1816", Justification = "This is a helper method for IDisposable.Dispose.")]
        protected void MarkAsDisposed()
        {
            GC.SuppressFinalize(this);
            Interlocked.Exchange(ref this.disposeStage, DisposalComplete);
        }
    }
}

#endregion Designer generated code
#pragma warning restore 1591, 0612