Partager via


Dispose Lessons

I recently read a number of excellent articles on Dispose. I will try to synthesise the practical advice I learned here:

Q. What is Dispose, and when is it needed?
A. Dispose is C#'s answer to deterministic resource deallocation. If your class owns unmanaged resources: 1. you must implement Dispose, 2. add a finalizer to the class 3. ensure the finalizer calls Dispose. If your class only owns managed resources, implementing Dispose is optional. If you don't implement it, you would be relying on the garbage collector to do the cleanup (indeterministic resource deallocation). If your class adds any event handlers, you should implement Dispose and unhook those event handlers in it, otherwise the handlers will keep instances of the class alive, and prevent garbage collection.

Q. How to implement Dispose?
A: Case1: Your class does not own any unmanaged resources. In this case apply the basic pattern:

 class MyDisposableClass : IDisposable
{
    #region IDisposable Members

    public void Dispose()
    {
        Dispose(true);  // true to indicate that managed resources should be disposed
        GC.SuppressFinalize(this);  // already disposed off the resources, so there is no need to run finalizer
    }

    #endregion

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // dispose managed resources
        }
        // no unmanaged resources to dispose
    }
}

Notes:
1. It would be much better to rename the disposing parameter as diposeManagedResources
2. It may seem that use of disposing parameter is unnecessary, since we know beforehand that this class has only managed resources to dispose. But think about what happens when a class inherits from this class.

Case2: Your class owns unmanaged resources. In this case apply the full pattern:

 class foo : IDisposable
{
    #region IDisposable Members

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

    #endregion

    protected virtual void Dispose(bool disposeManagedResources)
    {
        if (disposeManagedResources)
        {
            // dispose managed resources
        }
        // dispose unmanaged resources
    }

    ~foo()
    {
        Dispose(false); // never touch managed resources on finalizer thread
    }
}

If your class inherits from a class that implements IDisposable properly, all you need to do is override the Dispose(bool) method and ensure that
1. it calls the method on the base class (not necessary as per guidelines but I don't see any reason why this shouldn't be part of the guideline)
2. it disposes off any additional resources consumed by your class
Further, if your class owns unmanaged resources, but base class doesn't, then you would need to add a finalizer on your class.
If base class owns unmanaged resources, it should have a finalizer on it, and your class should NOT add a finalizer.

Random Notes:
1. The finalizer is run unpredictably by the Garbage collector (this is what we mean by indeterministic resource deallocation) on arbitrary thread
2. The finalizer implicitly calls the base class finalizer. The above destructor method really translates to following:

 protected override void Finalize()
    {
        try
        {
            Dispose(false);
        }
        finally
        {
            base.Finalize();
        }
    }

3. If your class inherits from a class that has a finalizer on it, DO NOT write a finalizer on your class.
4. The invocation of finalizer is not under your control. The finalizer is called by the Garbage Collector.
5. It is your responsibility to have the finalizer call Dispose. The Garbage Collector does NOT call Dispose automatically.

Further Reading (I have chosen 4 references from the many out there):
- https://www.codeproject.com/KB/dotnet/idisposable.aspx
- https://msdn.microsoft.com/en-us/library/66x5fx1b(VS.80).aspx
- https://haacked.com/archive/2005/11/18/ACloserLookAtDisposePattern.aspx
- https://msdn.microsoft.com/en-us/library/0s71x931(VS.71).aspx