The Dispose Pattern Step by Step

The Dispose Pattern in C# is all about the mechanics of effectively disposing of your instance fields that implement IDisposable and freeing up the unmanaged resources that you directly hold in your class.

I’d like to shed some light on many of the uncertainties when it comes to writing explicit teardown code for releasing your resources. This is not a complete beginner introduction to IDisposable and garbage collection in .Net. However, some very basic knowledge should be sufficient to follow along as I’ll also give many explanations and definitions along the way.

This post is a step by step guide for implementing the full-blown Dispose Pattern. It starts simple with a sealed class with a single IDisposable instance field. Then I gradually increase the complexity by creating additional scenarios like adding an unmanaged resource and introducing inheritance.

Have you been asking yourself any of the following questions?

  • Do I need a finalizer here?
  • What is the virtual Dispose(disposing) method needed for?
  • What if it’s already disposed of?
  • If someone derives from this class, how would he make sure the resources in the base class get cleaned up?

If you have, I think you’ll find the present article useful.

Let’s start with our first example. Following is a sealed class that contains an instance field which implements IDisposable:

public sealed class MyDisposableClass : IDisposable
{
    private readonly SqlConnection _sqlConnection;
    private bool _alreadyDisposed;

    public MyDisposableClass(string connectionString)
    {
        _sqlConnection = new SqlConnection(connectionString);
    }
    
    public void MyClassPublicMethod()
    {
        if (_alreadyDisposed)
        {
            throw new ObjectDisposedException(nameof(MyDisposableClass));
        }
        
        // Method implementation
    }
    
    public void Dispose()
    {
        if(_alreadyDisposed)
            return;
        
        _sqlConnection.Dispose();
        _alreadyDisposed = true;
    }
}

The SqlConnection class implements IDisposable. I’ve chosen to use it as an example, but the logic holds for pretty much every class that contains an IDisposable field.

The memory cleanup in this class is pretty straightforward, but it already shows some of the rules we need to follow when it comes to working with IDisposable fields. Let’s see what they are.

If our class contains an IDisposable field, we also need to implement IDisposable.Dispose() where we make sure we call the Dispose() method of our instance field. This rule is valid in almost all of the cases, with some infrequent exceptions.

Next, see the usage of the _alreadyDisposed field. It is used in a couple of places. First – to throw an ObjectDisposedException in our public method and second – to stop the Dispose() method execution in case of it being called more than once. And while the former can be considered as a good practice, the latter is essential. We need to be as defensive as possible when implementing our object disposal, making sure nothing bad will happen if the Dispose() method gets called more than once. We’ll see some good reasons for that further in this article when we add a finalizer and extract the common cleanup code in a single method.

Notice that our class holds a field implementing IDisposable, but it does not directly contain an unmanaged resource. That’s the reason why it does not need a finalizer. Finalizers are required if and only if you directly hold an unmanaged resource. That’s an important rule which we’ll discuss further in the following sections.

In my opinion, the use case I presented above is by far the most common one. If it matches your scenario – do this simple implementation and stop there. I think the disposal implementation happens to be overengineered quite often. Be careful not to fall into that trap.

Now, let’s add an unmanaged resource to our class and see the changes we need to perform. In the example below, I’m adding a pointer to a chunk of 100 MB of unmanaged memory.

public sealed class MyDisposableClass : IDisposable
{
    private readonly SqlConnection _sqlConnection;
    private readonly IntPtr _unmanagedPointer;
    private bool _alreadyDisposed;

    public MyDisposableClass(string connectionString)
    {
        _sqlConnection = new SqlConnection(connectionString);
        _unmanagedPointer = Marshal.AllocHGlobal(100 * 1024 * 1024);
    }
    
    public void MyClassPublicMethod()
    {
        if (_alreadyDisposed)
        {
            throw new ObjectDisposedException(nameof(MyDisposableClass));
        }
        
        // Method implementation
    }
    
    public void Dispose()
    {
        if(_alreadyDisposed)
            return;
        
        _sqlConnection.Dispose();
        _alreadyDisposed = true;
        
        Marshal.FreeHGlobal(_unmanagedPointer);

        GC.SuppressFinalize(this);
    }

    ~MyDisposableClass()
    {
        if(_alreadyDisposed)
            return;
            
        Marshal.FreeHGlobal(_unmanagedPointer);
    }
}

_unmanagedPointer may be something we need to interop with some unmanaged code from the “outside world” but all I do here is allocate the memory. The GC can’t release it as it’s just a pointer to unmanaged memory. That’s why the cleanup implementation is our responsibility. Hence the need for a finalizer in our class.

Before getting into the details of our example, let’s briefly review how the garbage collection process treats finalizers and why we need them alongside the Dispose() implementation.

The Garbage Collector doesn’t care about IDisposable and our Dispose() method. By implementing the IDiposable interface, we just tell our clients – “Hey, if you don’t call my Dispose() method right after you’re done with me, you’ll most probably leak resources.”. So, if Dispose() is the only thing we rely on to clean up our memory, this means that we depend on our clients always to be good citizens and do their job correctly by calling our Dispose() on time. This is, of course, not how the world works. Clients will forget to call Dispose(). That’s why we need stronger guarantees. And these guarantees come with the finalizer.  Finalizers are not called by humans, but the GC itself. They cannot be forgotten.

But how and when does the GC call the finalizers?

When the GC runs, it immediately removes from memory any garbage objects that do not have finalizers. All objects with finalizers remain in memory. These objects are added to a finalization queue, and the GC runs their finalizers in a separate thread. After the finalizer thread is done, the garbage objects can actually be removed from memory. They are moved up one generation because they survived a collection. They will be removed from memory on the next garbage collection of that higher generation.

Objects that need finalization stay in memory for far longer than objects without finalizers. But there is nothing you can do about it. If you hold an unmanaged resource – you need a finalizer. However, you can see how important it is not to add a finalizer for an object that doesn’t need one.

Let’s get back to our example. You can see that we clean up the unmanaged memory in two places – in the Dispose() method and the ~MyDisposableClass() finalizer. If the client of our class behaves well, he’ll call the Dispose() method, and all the resources will be cleaned up immediately. At that point, we know we’re done with the disposal, so we instruct the GC not to run the finalizer by calling GC.SuppressFinalize(this). If the client forgets about calling Dispose(), the resources cleanup will occur at some point in the future when the GC calls our finalizer.

You may be wondering why we don’t dispose of the SqlConnection resource in our finalizer. The reason is simple. Garbage objects’ finalizers can run in any order in an utterly nondeterministic manner. There are no guarantees at all which finalizer will be executed first between two objects that went out of scope. This means that by the time our finalizer is invoked, the SqlConnection might have already been finalized. This is why in the finalizer, we can only deal with our unmanaged resources that the GC doesn’t know how to handle. Managed objects are not in our control at this point.

Let’s have a quick recap. In the Dispose() method, we clean up the managed and unmanaged resources. In the finalizer, we take care of unmanaged resources only. We can factor out this logic into a single method and call it both from the Dispose() method and the finalizer. We’ll add an input flag disposing that would indicate whether our clean up method called from Dispose() or the finalizer.

Here’s what the implementation looks like:

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

private void Dispose(bool disposing)
{
    if(_alreadyDisposed)
        return;

    if (disposing)
    {
        _sqlConnection.Dispose();
        _alreadyDisposed = true;
    }
    
    Marshal.FreeHGlobal(_unmanagedPointer);
}

~MyDisposableClass()
{
    Dispose(disposing:false);
}

I would understand if some of you are not happy with this refactoring. The main reason being the disposing flag. In many cases, flags in the method signatures are considered code smells as they are a direct indicator that the method is doing more than one thing. I see the point in such an argument, but there is a more significant benefit for extracting out the disposal logic in a method like that. And this is related to inheritance.

Imagine we make our sample class inheritable by removing the sealed keyword. Any class that inherits from it can have its own IDisposable and/or unmanaged resources to take care of. It may need to implement its own finalizer. But there is one more thing it’s responsible for – calling the cleanup logic of its parent. By calling the parent’s disposal logic, we make sure than both the derived and the parent classes correctly tidy up their memory.

But how exactly do we tell the parent to dispose of its resources? That’s simple – we just make our new Dispose(disposing) method protected and virtual. By doing that, this method becomes a hook for the derived classes to run their own cleanup logic. And once they are done, they have the responsibility to call the base implementation. This is how, from bottom to top, every class in the inheritance chain will be disposed of.

Let’s see how to do that from a technical perspective. First, we need to remove the sealed keyword and make our Dispose(disposing) method protected:

public class MyDisposableClass : IDisposable
{   
    /* … */

    protected virtual void Dispose(bool disposing) 

    /* … */
}

And here is a sample derived class that has its own IDisposable field and an unmanaged resource.

public class MyDerivedDisposableClass : MyDisposableClass
{
    private readonly FileStream _fileStream;
    private readonly IntPtr _unmanagedPointer;
    private bool _alreadyDisposed;


    public MyDerivedDisposableClass(string path, string connectionString) : base(connectionString)
    {
        _fileStream = new FileStream(path, FileMode.Open);
        _unmanagedPointer = Marshal.AllocHGlobal(100 * 1024 * 1024);
    }
    
    public void MyDerivedClassPublicMethod()
    {
        if (_alreadyDisposed)
        {
            throw new ObjectDisposedException(nameof(MyDerivedDisposableClass));
        }
        
        // Method implementation
    }

    protected override void Dispose(bool disposing)
    {
        if(_alreadyDisposed)
            return;
        
        if (disposing)
        {
            _fileStream.Dispose();
        }
        
        Marshal.FreeHGlobal(_unmanagedPointer);
        _alreadyDisposed = true;
        
        base.Dispose(disposing); // The disposal here is done, so let the parent do the same. 
    }

    ~MyDerivedDisposableClass()
    {
        Dispose(disposing:false);
    }
}

Notice how the derived class also holds its’ own _alreadyDisposed flag. The idea is that we keep the scope of this flag focused on a single class implementation, which, in my opinion, is better from a maintainability perspective.

In the end, I’d like to summarize the sequence of actions that are executed when a client of the derived class calls Dispose() directly or indirectly via the using construct like this:

using (var myDerivedClass = new MyDerivedDisposableClass("path", "connectionString"))
{
    // Do something with the class
} // myDerivedClass.Dispose() is called here
  1. The client calls the public Dispose() method e.g. the IDisposable implementation. This method is part of the derived class public interface as it’s inherited from the base class.
  2. Dispose() calls the protected virtual Dispose(disposing) method with disposing=true. At this point, the derived class implementation of the method is called due to polymorphism.
  3. The derived class Dispose(disposing) takes care of cleaning all the managed and unmanaged resources.
  4. The derived class calls the parent implementation of Dispose(disposing).
  5. The base class cleans up its managed and unmanaged resources.
  6. Both the derived and the base class Dispose(disposing) executions are completed now and the program flow returns to the public Dispose() method.
  7. Dispose() calls GC.SupressFinalize(this) and the execution completes.

Make sure you understand the sequence of steps above. This is how the classes collaborate in order to dispose of their resources adequately.

And this is what the Dispose Pattern is all about.

Thank you for reading.

Resources

  1. C# 7.0 in a Nutshell: The Definitive Reference, Joseph Albahari
  2. Effective C#, Bill Wagner
  3. https://stackoverflow.com/questions/3607213/what-is-meant-by-managed-vs-unmanaged-resources-in-net
  4. https://bytes.com/topic/c-sharp/answers/276059-what-unmanaged-resources
  5. https://stackoverflow.com/questions/3433197/what-exactly-are-unmanaged-resources
2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
André Vieira
André Vieira
4 years ago

Excellent read, man. Thanks for that!

Site Footer

Subscribe To My Newsletter

Email address