BlackWasp
.NET Framework
.NET 1.1+

Implementing IDisposable

Classes that use unmanaged resources, or that hold references to managed objects that themselves use unmanaged resources, should always implement the IDisposable interface. The correct use of this interface ensures the efficient release of resources.

What is IDisposable?

IDisposable is an interface defined in the System namespace. The interface defines a single method, named "Dispose", that is used to perform clean-up activities for an object when it is no longer required. The method may be called manually, when an object's work is finished, or automatically during garbage collection.

The IDisposable interface was designed to provide a standard way to release unmanaged resources. These are resources that the garbage collector does not manage on our behalf and is unable to clean up automatically. They include items such as streams, files, database connections and handles. If the memory and system resources that they use are not properly released, your software may suffer from memory leaks or problems due to locked resources.

If you develop a class that owns unmanaged resources, you would normally include a finalizer or destructor that releases those resources when objects go out of scope. However, as the actual execution time of the finalizer is not predictable, the resources can stay allocated for longer than is desirable. By implementing the IDisposable interface, you allow the disposal of such resources to be requested immediately, rather than waiting for the garbage collector.

If you create a class that does not use unmanaged resources then you should not implement IDisposable. However, you should consider whether class-scoped variables within your code are of types that themselves implement the interface. For example, if you hold a Stream object at this scope, you should implement IDisposable and call the Dispose method on this object when releasing resources. This ensures that the unmanaged file handle controlled by the Stream object is released as early as possible.

NB: Sometimes the objects that a class utilises are shared. You should take care when disposing these as they become unusable once released. If the object is still required elsewhere, exceptions will occur.

Implementing IDisposable

There are several options to consider when implementing the IDisposable interface. In this article we will start with the basic pattern that should be used. We will then modify this pattern to show how to implement the interface when a finalizer is required and in an inheritance relationship. You can download the example classes using the link at the top of this page.

Simple Implementation

The first example of the IDisposable interface will examine the basic pattern for its implementation. We will start with a simple class. To follow the examples, create a new console application and add a new class named SimpleDisposableResource. Copy the code below into this class.

class SimpleDisposableResource
{
    Stream _stream;

    public SimpleDisposableResource()
    {
        _stream = File.OpenRead(@"c:\test\test.txt");
    }

    public long FileLength()
    {
        return _stream.Length;
    }
}

As you can see, this class fragment contains a private Stream object, which is initialised in the constructor. A method allows the length of the file to be obtained.

The problem with this class is that Stream objects use unmanaged resources. If this class is instantiated and later allowed to simply go out of scope, the streams may not be cleaned up immediately and resources will be utilised unnecessarily. If we implement the IDisposable interface using the standard pattern, we can allow these resources to be released on demand.

To implement the interface correctly, we need to make several modifications to the class. The first of these is to add the interface name, as follows:

class SimpleDisposableResource : IDisposable

Once a class has been disposed, all of its resources should be considered unreachable. Attempts to continue using the class may be invalid and should be prevented. We can add this limitation by making two changes. Firstly, we will add a private Boolean variable that holds "false" when the object is accessible and "true" once it has been disposed.

Add the variable declaration immediately after the Stream definition.

private bool _disposed;

The state of the object can be checked when any attempt to use a class member is made. If the object has been disposed, we will throw an ObjectDisposedException, providing the object name, to indicate the error. This check should be made in all public members of the class. For the sample, add the following "if" statement to the start of the FileLength method:

if (_disposed) throw new ObjectDisposedException("MyObject");

We now need to implement the Dispose method. This is the method that is called when the object is to free its resources. The standard manner of implementing the method is to create two versions of Dispose, even though only one is defined in the interface. The first version accepts a Boolean parameter that is used to signify whether the code has been called manually or automatically. If called manually, all managed and unmanaged resources can be released. If the method was called automatically, via the garbage collection process, the managed resources will already have been dealt with so only the unmanaged resources are cleared.

In the simple example, no unmanaged resources are being used directly, so just the stream's Dispose method needs to be called. Add the following method:

protected virtual void Dispose(bool disposing)
{
    if (!_disposed)
    {
        if (disposing)
        {
            if (_stream != null) _stream.Dispose();
        }

        _stream = null;
        _disposed = true;
    }
}

There are two important points to note in the parameterised method. Firstly, it is protected and virtual. This prevents external classes from calling the method but allows it to be accessed and overridden by subclasses. Secondly, it is possible that this method will be called several times, so it returns immediately if the object has already been disposed. The flag to indicate this is set on completion of the method.

The Dispose method may perform operations that could cause exceptions. These should be handled to allow for graceful recovery. You should only throw critical exceptions from Dispose. However, these will not be caught if the "disposing" flag is false.

The final task is to add the parameterless version of Dispose. This is the public version that can be called manually. The method calls the parameterised version, passing "true" to indicate that the procedure has been started manually. Secondly, it calls the SuppressFinalize method of the garbage collector. As the class does not require a finalizer, and any subclasses' finalizers should call Dispose, this prevents the finalizer from executing and limits any associated performance degradation.

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

Using the Interface

With the class now correctly implementing the IDisposable pattern you can free up its resources on demand. The following code, added to the Main method, shows how. Note that the third line of code causes an exception as we are trying to access a member after the object was disposed.

SimpleDisposableResource sdr = new SimpleDisposableResource();
sdr.Dispose();
Console.WriteLine(sdr.FileLength());    // ObjectDisposedException

Implementing IDisposable with a Finalizer

If your class uses unmanaged resources directly, these should be released within the Dispose method. As we have seen, the method can be called directly by the user. Where unmanaged resources are present we must ensure that they can also be released by the garbage collector. This is achieved by adding a finalizer and having the finalizer call the Dispose method with a parameter of "false". The false value indicates that the method was not called manually.

The following code shows the entire implementation for a class that uses an IntPtr structure, which holds a handle requiring manual release. The sample also includes a Stream object to show the differences between the release of the managed and unmanaged resources. Note the addition of the finalizer, named ~DisposableWithFinalizer and that the handle is released whether the method is called automatically or manually.

class DisposableWithFinalizer : IDisposable
{
    Stream _stream;
    IntPtr _handle;
    private bool _disposed;

    public DisposableWithFinalizer()
    {
        _stream = File.OpenRead(@"c:\test\test.txt");
        _handle = new IntPtr();
    }

    public long FileLength()
    {
        if (_disposed) throw new ObjectDisposedException("MyObject");
        return _stream.Length;
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                // Dispose managed resources
                if (_stream != null) _stream.Dispose();
            }

            // Dispose unmanaged resources
            _handle = IntPtr.Zero;

            _stream = null;
            _disposed = true;
        }
    }

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

    ~DisposableWithFinalizer()
    {
        Dispose(false);
    }
}

Implementing IDisposable in an Inheritance Hierarchy

When creating an inheritance hierarchy where a base class implements IDisposable, you do not need to implement the interface in the subclasses. If you did, this would lead to unnecessary calls to the Dispose method, creating a performance penalty. Instead, if the subclass uses unmanaged resources, override the parameterised Dispose method and use this to clean up your resources. To ensure that the base class is also released correctly, the method should call its base equivalent.

The following example contains a class that is derived from the simple code shown earlier

class DisposableSubclass : SimpleDisposableResource
{
    Stream _stream;

    public DisposableSubclass()
    {
        _stream = File.OpenRead(@"c:\test\test.txt");
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            _stream.Dispose();
        }

        _stream = null;

        base.Dispose(disposing);
    }
}

Further Notes

This pattern for disposing of resources described is not thread-safe. In most situations this is not an issue. However, when thread safety is required, you should implement a lock around the dispose operations and in any members that utilise the resources.

Whenever you use a class that implements IDisposable, you should call Dispose when you no longer need its objects. This ensures that its resources are freed up at the earliest time possible. You should never assume that an object that uses unmanaged resources will be disposed automatically, as it is possible that the garbage collector will never call the finalizer.

Link to this Page6 August 2008
TwitterTwitter RSS Feed RSS