Previous Page
Next Page

Resource Management

Sometimes it's inadvisable to release a resource in a destructor; some resources are just too valuable and too scarce to lie around unreleased for arbitrary lengths of time. Scarce resources need to be released, and they need to be released as soon as possible. In these situations, your only option is to release the resource yourself. A disposal method is a method that disposes of a resource. If a class has a disposal method, you can call it explicitly and thereby control when the resource is released.

Disposal Methods

An example of a class that implements a disposal method is the TextReader class from the System.IO namespace. This class provides mechanisms to read characters from a sequential stream of input. TextReader contains a virtual method called Close, which closes the stream. The StreamReader class (which reads characters from a stream, such as an open file) and the StringReader class (which reads characters from a string) both derive from TextReader, and both override the Close method. Here's an example that reads lines of text from a file by using the StreamReader class, and then displays them on the screen:

TextReader reader = new StreamReader(filename);
string line;
while ((line = reader.ReadLine()) != null)
{
    Console.WriteLine(line);
}
reader.Close();

The ReadLine method reads the next line of text from the stream into a string. The ReadLine method returns null if there is nothing left in the stream. It's important to call Close when you have finished with reader to release the file handle and associated resources. However, there is a problem with this example; it's not exception-safe. If the call to ReadLine (or WriteLine) throws an exception, the call to Close will not happen; it will be bypassed. If this happens often enough, you will run out of file handles and be unable to open any more files.

Exception-Safe Disposal

One way to ensure that a disposal method (such as Close) is always called, regardless of whether there is an exception, is to call the disposal method inside a finally block. Here's the previous example coded by using this technique:

TextReader reader = new StreamReader(filename);
try
{
    string line;
    while ((line = reader.ReadLine()) != null)
    {
        Console.WriteLine(line);
    }
}
finally
{
    reader.Close();
}

Using a finally block like this works, but it has several drawbacks that make it a less than ideal solution:

The using statement is designed to solve all these problems.

The using Statement

The using statement provides a clean mechanism for controlling the lifetimes of resources. You can create an object, and this object will be destroyed when the using statement block finishes.

IMPORTANT
Do not confuse the using statement shown in this section with the using directive that brings a namespace into scope. It is unfortunate that the same keyword has two different meanings.

The syntax for a using statement is as follows:

using ( type variable = initialization ) embeddedStatement

Here is the best way to ensure that your code always calls Close on a TextReader:

using (TextReader reader = new StreamReader(filename))
{
    string line;
    while ((line = reader.ReadLine()) != null)
    {
        Console.WriteLine(line);
    }
}

This using statement is precisely equivalent to the following translation:

{
    TextReader reader = new StreamReader(filename);
    try
    {
        string line;
        while ((line = reader.ReadLine()) != null)
        {
            Console.WriteLine(line);
        }
       }
    finally
    {
        if (reader != null)
        {
            ((IDisposable)reader).Dispose();
        }
    }
}
NOTE
Note the outer block scope. This arrangement means that the variable you declare in a using statement goes out of scope at the end of the embedded statement.

The variable you declare in a using statement must be of a type that implements the IDisposable interface. The IDisposable interface lives in the System namespace and contains just one method called Dispose:

namespace System
{
    interface IDisposable
    {
        void Dispose();
    }
}

It just so happens that the StreamReader class implements the IDisposable interface, and its Dispose method calls Close to close the stream. You can use a using statement as a clean, exception-safe, robust way to ensure that a resource is always automatically released. This solves all of the problems that existed in the manual try/finally solution. You now have a solution that:

Calling the Dispose Method from a Destructor

When writing a class, should you write a destructor, or implement the IDisposable interface? A call to a destructor will happen but you just don't know when. On the other hand you know exactly when a call to the Dispose method happens, but you just can't be sure that it will actually happen, because it relies on the programmer remembering to write a using statement. However, it is possible to ensure that the Dispose method always runs by calling it from the destructor. This acts as a useful backup. You might forget to call the Dispose method, but at least you can be sure that it will be called, even if it's only when the program shuts down. Here's an example of how to do this:

class Example : IDisposable
{
    ...
    ~Example()
    {
        Dispose();
    }

    public virtual void Dispose()
    {
        if (!this.disposed)
        {
            try {
                // release scarce resource here
            }
            finally {
                this.disposed = true;
                GC.SuppressFinalize(this);
            }
        }
    }

    public void SomeBehavior() // example method
    {
        checkIfDisposed();
        ...
    }
    ...
    private void checkIfDisposed()
    {
        if (this.disposed)
        {
            throw new ObjectDisposedException("Example");
        }
    }

    private Resource scarce;
    private bool disposed = false;
}

Notice the following:


Previous Page
Next Page