Friday, 5 October 2012

Working with IDisposable Part 4: Implementing IDisposable

Though IDisposable is a regular interface, there are specific conventions with regards to how to implement it. MSDN has a good example, and StackOverflow has a very detailed explanation. I will try to be brief and go over the essentials.

When to Implement IDisposable

There are two scenarios where you should implement IDisposable:

  1. When your class consumes an unmanaged resource
  2. When your class has a field which is IDisposable

In my experience you don't encounter the first that often since consuming unmanaged resources is a generally non-platform independent practice, but it is the primary reason for IDisposable. The second, however, is encountered quite often.

Basically, when you are using IDisposable in such a way that it cannot be used in a using statement, it will have to be made a field (or automatic property). In this case, the class should implement IDisposable to take care of the disposing of those fields.

Note: By unmanaged resource I don't mean things like Bitmap or SqlConnection, I mean something that will require an interop call to CloseHandle or something.

Implementing IDisposable

IDisposable has only one method: void Dispose(); But in order to properly implement you will have to do a bit more. Here is the canonical implementation:

public class SomeClass : IDisposable
{
    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            //Dispose objects which implement IDisposable here
        }

        //Free unmanaged resources here
    }

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

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

The Meaning of all This

As you can see, there are two more methods, other than IDisposable.Dispose. The reason for this is to fulfill the mandate of an IDisposable interface: to release unmanaged resources under all circumstances. Because garbage collection is, in large part, a process which cannot be controlled by the program, it can cause some unusual situations.

An example would be where the class is collected after it's members have been disposed. If you tried to dispose one of these members it would cause an exception. This is the main reason behind the form of the implementation.

The disposing parameter on the virtual Dispose tells it whether it is being called because the garbage collector is busy cleaning up and called the Finalize method (disposing = false), or if it is being disposed by a call to IDisposable.Dispose (disposing = true). Using this information, we will only free managed resources if Dispose has been called from code.

Note: when implementing a sealed IDisposable class, Dispose(bool) should be private instead of protected.

In the case where Dispose is called from code, we also tell the garbage collector to refrain from calling Finalize (because it is no longer necessary), this is where GC.SuppressFinalize(this) comes in. All it does is tell the garbage collector not to call Finalize when the object is garbage collected.

The only other thing to account for is to handle Dispose being called more than once. Though calling Dispose more than once is something to avoid when writing code, it can happen and it would not hurt to handle this case quietly.

There are two ways to achieve this. The most logical solution is to declare a boolean field called _isDisposed or something, and not dispose if it has been set to true. The other way is to dispose consumed objects like this:

if (databaseConnection != null)
{
    databaseConnection.Dispose();
    databaseConnection = null;
}

This way also ensures that the objects will not be used after they have been disposed.

No comments:

Post a Comment