BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

.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's finalizers should call Dispose, this prevents the finalizer from executing and limits any associated performance degradation.

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