Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Implementing IDisposable and the Dispose Pattern Properly

0.00/5 (No votes)
30 Dec 2007 10  
Explains how to properly implement the IDisposable interface, the Dispose Pattern, and deterministic finalization.

Introduction

In most modern programming languages, memory is allocated on the heap or the stack. Memory allocated on the stack stores local variables, parameters, return values, and is generally managed by the operating system. Memory allocated on the heap is handled differently by different programming languages. In C and C++, memory must be managed manually, while in C# and Java, memory is managed automatically.

Even though manual memory management is easier for the language runtime, it adds complexity for the programmer, which can lead to bugs and coding errors due to improper memory management and object lifecycle maintenance.

Automatic memory management is known as garbage collection. Even though garbage collection is harder for the language runtime, it reduces the complexity for the programmer, and helps reduce the number of bugs and coding errors caused by manual memory management.

Garbage Collection in .NET

The garbage collector (GC) in .NET is a core part of the .NET Common Language Runtime (CLR) and is available to all .NET programming languages. The GC was never meant to manage resources; it was designed to manage memory allocation, and it does an excellent job at managing memory allocated directly to native .NET objects. It was not designed to deal with unmanaged memory and operating system allocated memory, so it becomes the responsibility of the developer to manage these resources.

In non-garbage collected languages, as soon as the object's lifetime ends, either through the completion of the local block of execution or when an exception is thrown, the destructor kicks in and the resources are automatically released. While garbage collection simplifies the management of the object lifecycle, it does prevent an object from knowing when it will be collected, which means that it is difficult to ensure that the resources held by that object are released early. Before the memory associated with an object is reclaimed by the GC, the Finalize method (if it is present) is invoked. This method is not tied to the lifetime of the object, so the timing of when (or even if) Finalize is called is undefined. This is what is meant by saying that the GC performs non-deterministic finalization.

The lack of deterministic finalization is a common complaint about garbage collected languages. The primary goal when dealing with unmanaged resources is to make use of them in the most efficient manner. Lacking deterministic finalization would seem to violate that goal. The problem with non-deterministic finalization is that it happens at an undetermined point in the future, usually when certain memory-exhaustion thresholds are met. If the object holds on to expensive resources, such as file handles or database connections, those resources are only released when the object is finalized. Finalization is the "safety net" provided by the CLR to help ensure that objects are cleaned up if something goes wrong (either the program crashes, or through coding omissions).

Fortunately, the .NET Framework provides many abstractions to hide the complexity of dealing with unmanaged resources. In fact, many of the CLR classes make use of these unmanaged resources on your behalf and handle all of the resource management transparently.

The CLR does provide a way to implement deterministic finalization through the IDisposable interface, a Dispose method for explicit cleanup, and in some cases, a Finalize method for implicit cleanup. This is commonly referred to as the Dispose Pattern (or the IDisposable Pattern).

Implementing this pattern correctly is critical to ensuring the proper, timely cleanup of resources, and also provides users with a deterministic, familiar way of disposing those resources.

IDisposable and Resource Cleanup

The IDisposable interface is defined as:

public interface IDisposable
{
   void Dispose();
}

In order to use the IDisposable interface, you would declare a class like this:

public class MyClass : IDisposable
{
    public void Dispose()
    {
        // Perform any object clean up here.

        // If you are inheriting from another class that
        // also implements IDisposable, don't forget to
        // call base.Dispose() as well.
    }
}

Explicit Cleanup

In every case where a type owns resources, or owns types that own resources, you should give users the ability to explicitly release those resources by providing a public Dispose method. This alleviates some of the reliance on the GC, and provides users a way to deterministically reclaim resources.

Implicit Cleanup

Implicit cleanup in .NET 2.0 should be provided whenever possible by protecting resources with a SafeHandle. In .NET 1.0 or 1.1, this class does not exist, so you will need to implement a finalizer. In .NET 2.0, thanks to the SafeHandle class, implementing finalizers is rarely needed. If additional finalization is needed (or you are working with .NET 1.0 or 1.1), you can implement the protected Finalize method using the language specific syntax. The runtime will call Finalize for you non-deterministically as part of the GC's finalization process, providing a last chance for your object to release resources at the end of its lifetime.

You really don't want to write a finalizer if you don't have to. Writing them properly can be very difficult, and by writing one, it makes your class more expensive to use, even if the finalizer is never called. All objects that implement a finalizer must be put on a list of finalizable objects (called the finalization queue) maintained by the GC. This is handled automatically, but it can't be avoided. When you must cleanup resources, you almost always want to provide it in a Dispose method, not in a finalizer. However, when you do need a finalizer, you want it in addition to, not instead of, Dispose.

Syntax Note and Framework Version Difference

The Dispose and Finalize methods serve very different purposes, and have different language syntax for declaring them.

Language Destructor Syntax Finalizer Syntax
C# public void Dispose() ~T()
C++ (.NET 2.0) ~T() !T()
C++ (.NET 1.0/1.1) public void Dispose() ~T()
Visual Basic (.NET) Public Sub Dispose() Implements IDisposable.Dispose Protected Overrides Sub Finalize()

The Dispose Pattern

The Dispose Pattern is defined by the IDisposable interface.

If your class is sealed (NotInheritable in Visual Basic), then you don't need to follow the Dispose Pattern; you should implement the Dispose and Finalize methods using simple methods. You do still need to follow the rules regarding how to implement Dispose and Finalize, you just don't need to implement the full pattern.

For classes that aren't sealed and need to do resource cleanup, you should follow the Dispose Pattern exactly. The pattern has been designed to ensure reliable, predictable cleanup, to prevent temporary resource leaks, and to provide a standard, unambiguous pattern for disposable classes. It also aids subclasses to correctly release base class resources.

Syntax Note

If you are using C++ (.NET 2.0), you can simply write the usual destructor (~T()) and the compiler will automatically generate all of the code to implement the Dispose Pattern. If you need to write a finalizer (!T()), you should share the code by putting as much of the work into the finalizer as it is able to handle (the finalizer can't access other objects, so don't put code there that needs them), put the rest in the destructor, and have the destructor explicitly call the finalizer.

The Dispose Pattern should be implemented on classes with disposable subtypes, even if the base type does not own any resources. This helps programmers who are using your base class to dispose off further derived instances properly. If you are inheriting from a class that already implements IDisposable, don't re-implement it. You only need to implement IDisposable if a class in your inheritance chain doesn't already implement it.

A great example of this is the System.IO.Stream class. Even though it is abstract and doesn't hold any resources, its subclasses do. As a result, it implements the Dispose Pattern to aid the subclasses.

public abstract class Stream : MarshalByRefObject, IDisposable
{
    public virtual void Close();
    public void Dispose();
    protected virtual void Dispose(bool disposing);
}

// Implicitly gains the Dispose pattern because
// Stream inherits from it, so there is no
// need to re-implement the pattern.
public class MemoryStream : Stream
{
}

// Implicitly gains the Dispose pattern because
// Stream inherits from it, so there is no
// need to re-implement the pattern.
public class FileStream : Stream
{
}

The public void Dispose() method should be left final (in other words, don't make this a virtual method), and should always look like this:

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

The order of the two calls is important, and shouldn't be changed. This order ensures that GC.SupressFinalize() only gets called if the Dispose operation completes successfully. When Dispose calls Dispose(true), the call may fail, but when Finalize is called later, it calls Dispose(false). In reality, these are two different calls which can execute different portions of the code, so even though Dispose(true) fails, Dispose(false) may not.

All of your resource cleanup should be contained in the Dispose(bool disposing) method. If necessary, you should protect the cleanup by testing the disposing parameter. This should happen for both managed and unmanaged resources. The Dispose(bool disposing) runs in two distinct scenarios:

  • If disposing equals true, the method has been called directly or indirectly by a user's code. Managed and unmanaged resources can be disposed.
  • If disposing equals false, the method has been called by the runtime from inside the finalizer, and you should not reference other objects. Only unmanaged resources can be disposed.

To give your base classes a chance to cleanup resources, you should always make a call to your base class' Dispose(bool disposing) (if it is available) as the last operation in your own Dispose(bool disposing) method. Make sure to pass the same value for disposing to the base class' Dispose(bool disposing) method.

protected virtual void Dispose(bool disposing)
{
    if (!disposed)
    {
        if (disposing)
        {
            // Dispose managed resources.
        }

        // There are no unmanaged resources to release, but
        // if we add them, they need to be released here.
    }
    disposed = true;

    // If it is available, make the call to the
    // base class's Dispose(Boolean) method
    base.Dispose(disposing);
}

If your object is responsible for at least one resource that does not have its own finalizer, you should implement a finalizer in your object. If your base class already overrides Finalize to follow this pattern, you shouldn't override it yourself. Your Finalize method should make a single virtual call to Dispose(false). All of the finalization cleanup logic should be in the Dispose(bool disposing) method.

If any of the base classes in your inheritance chain implements the IDisposable interface, overrides void Dispose(), or overrides Finalize, you should simply override Dispose(bool disposing), adding your cleanup logic and making sure to call base.Dispose(disposing) as the last statement.

Syntax Note

Dispose should be considered a reserved word in order to help prevent confusion, so you shouldn't create any other variations other than:

  • void Dispose()
  • void Dispose(bool disposing)

Some Examples

The simplest implementation of the Dispose Pattern does not include a Finalize method. This is the pattern that you will follow for the majority of types.

public class SimpleCleanup : IDisposable
{
    // some fields that require cleanup
    private SafeHandle handle;
    private bool disposed = false; // to detect redundant calls

    public SimpleCleanup()
    {
        this.handle = /*...*/;
    }

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

            disposed = true;
        }
    }

    public void Dispose()
    {
        Dispose(true);
    }
}

A more complex implementation of the pattern is a base-class implementation that implements a Finalizer. The ComplexCleanupExtender shows how you would hook into the Dispose and Finalize cycle from a subclass. Notice that it does not re-implement Dispose or Finalize.

public class ComplexCleanupBase : IDisposable
{
    // some fields that require cleanup
    private bool disposed = false; // to detect redundant calls

    public ComplexCleanupBase()
    {
        // allocate resources
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // dispose-only, i.e. non-finalizable logic
            }

            // shared cleanup logic
            disposed = true;
        }
    }

    ~ComplexCleanupBase()
    {
        Dispose(false);
    }

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

public class ComplexCleanupExtender : ComplexCleanupBase
{
    // some new fields that require cleanup
    private bool disposed = false; // to detect redundant calls

    public ComplexCleanupExtender() : base()
    {
        // allocate more resources (in addition to base’s)
    }

    protected override void Dispose(bool disposing)
    {
       if (!disposed)
       {
            if (disposing)
            {
                // dispose-only, i.e. non-finalizable logic
            }

            // new shared cleanup logic
            disposed = true;
        }

        base.Dispose(disposing);
    }
}

Implementing Dispose

If your class has a finalizer, you should always implement Dispose. This gives users of your class the ability to explicitly clean up the resources which the finalizer is responsible for. You can also implement Dispose on classes that don't have finalizers. The only exception to this rule is for value types, which can't have either a finalizer or a destructor.

It is important to remember that Dispose may be called more than once. If that happens, you should never throw an exception, but you may choose to ignore the subsequent calls.

Language Caution

The C# automatically generated finalizer chaining does call Dispose(bool) multiple times, which is why it is especially important to implement the pattern only once in the inheritance chain.

If your object controls any disposable types, it should call Dispose on those types in your own Dispose method. If this is done from inside the Dispose(bool disposing) method, you should only do this if disposing is true. If your object doesn't control a disposable type, you shouldn't attempt to dispose off it as other code could still be referencing it.

public class MyClass : IDisposable
{
   private TextReader reader;
   private bool disposed = false; // to detect redundant calls

   public MyClass()
   {
       this.reader = new TextReader();
   }

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

           disposed = true;
       }
   }

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

Language Note

In C++, you can follow the normal pattern of storing the TextReader by value. The destructor will automatically call reader.~TextReader() following the normal C++ semantics.

You should not throw exceptions from within Dispose except under critical situations. If executing a Dispose(bool disposing) method, never throw an exception if disposing is false; doing so will terminate the process if executing inside a finalizer context.

Recreating an object that has already been disposed is usually very difficult, and generally should not be done. Because of this, you should consider making your object unusable after calling Dispose by throwing an ObjectDisposedException. If you are able to reconstruct the object, you should warn users that they must potentially re-dispose of an object multiple times.

If the semantics of your object allows for it, implement a Close method for your resource cleanup. This method implementation should be identical to Dispose as most developers will not think to call both Close and Dispose. In these cases, the Dispose method should be a call to Close.

public class MyClass : IDisposable
{
   private TextReader reader;
   private bool disposed = false; // to detect redundant calls

   public MyClass()
   {
       this.reader = new TextReader();
   }

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

           disposed = true;
       }
   }

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

   public void Dispose()
   {
       this.Close();
   }
}

There are times, however, when it is necessary to provide different implementations for Close and Dispose. This is usually the case when an object can be opened and closed multiple times without recreating the instance. The .NET Framework uses this pattern with the System.Data.SqlClient.SqlConnection class. This class allows you to open and close a connection multiple times, but the instance still needs to be disposed after you are done using it.

Advanced Implementation

As an alternative, you can release the resources in Close and lazily reacquire them later in a subsequent Open. If you choose to do this, you should call GC.ReRegisterForFinalize(this) when reacquiring them. You will also potentially need to reconstruct the object state.

If you have cyclic references in your objects, you should set those references to null before calling Dispose.

public class CyclicClassA : IDisposable
{
    private TextReader myReader;
    private CyclicClassB cycle;

    public void Dispose()
    {
        if (myReader != null)
        {
            ((IDisposable)myReader).Dispose();
            myReader = null;
        }

        if (cycle != null)
        {
            CyclicClassB b = cycle;
            cycle = null;
            b.Dispose();
        }
    }
}

public class CyclicClassB : IDisposable
{
    private Bitmap bmp;
    private CyclicClassA cycle;

    public void Dispose()
    {
        if (bmp != null)
        {
            bmp.Dispose();
            bmp = null;
        }

        if (cycle != null)
        {
            CyclicClassA a = cycle;
            cycle = null;
            a.Dispose();
        }
    }
}

In this example, given an instance of CyclicClassA a and CyclicClassB b, if a.cycle = b and b.cycle = a, transitive disposal would ordinarily cause an infinite loop. In the above example, notice that the object’s state is nulled out first to prevent such a cyclic loop from happening.

You should also consider setting any large and expensive managed objects that you own to null before calling Dispose. This is seldom necessary, but it can help reduce the lifetime of the object by making it eligible for garbage collection sooner. Of course, the definition of large and expensive is subjective, and should be based on performance profiling and measurement.

If you are creating a value type, you should avoid making it disposable, and it should never contain unmanaged resources directly.

Implementing Finalize

Finalizers are very difficult to implement correctly, mainly because you cannot make certain (normally valid) assumptions about the state of the system during their execution. If you decide to implement a finalizer, you should make sure to do it properly. There is both a performance and code complexity cost associated when implementing a finalizer, so consider it very carefully before you do so. When possible, you should use resource wrappers, such as SafeHandle, to encapsulate unmanaged resources.

Finalization increases the cost and duration of your object's lifetime since each finalizable object must be placed on the finalization queue when it is allocated. This happens even if the finalizer is never called. This has the side effect of making the GC work harder to dispose of your object, and causes it to be kept alive longer.

If you must implement a finalizer, make the Finalize method protected, not public or private. For C# and C++, the compiler will do this automatically for you.

These rules do not apply just to the Finalize method, but to any code that executes during finalization. If you implement the Dispose Pattern described in this article, it also applies to the code that executes inside Dispose(bool disposing) when disposing is false.

Since finalizers run non-deterministically, there is no ordering among them, so you can't rely on them being executing in a specific order. You should not access any finalizable objects your type may have a reference to, because they may have already been finalized. As a result, you should only free unmanaged resources that your object owns.

Implementation Caution

This caution also extends to static variables. Accessing a static variable that refers to a finalizable object is not safe. This also applies to calling a static method that may use values stored in static variables. In .NET 1.1 and later, you can use Environment.HasShutdownStarted to detect if your finalizer is running. It is okay to access unboxed value types.

You should never directly call the Finalize method. In C#, this is not allowed, but it is possible in VB and C++. In fact, it is legal Intermediate Language (IL) syntax. Even though you should never directly call Finalize, it should still be able to handle situations in which it is called more than once. In order to do this, you may need a way to detect that finalization has already occurred.

It is always possible that other objects in the finalization queue might still have live references to your object, potentially even after your finalizer has run. You should be able to detect when your object is in an inconsistent state during the execution of any method. If you define a finalizer that closes a resource used by your type, you may need to call GC.KeepAlive at the end of any instance method that doesn't use the this pointer (Me in Visual Basic) after doing some operation on that resource. This also implies that you need to be able to handle partially constructed instances, which can happen when the constructor never completes. If the constructor throws an exception, Finalize will still be called. In this instance, it is possible that some of the fields have not been initialized.

For example, in the following code, list may be null if the constructor throws an exception before list is assigned to.

public class MyClass
{
    private ArrayList list;

    public MyClass()
    {
        // do some work that might throw an exception
        list = new ArrayList();
    }

    ~MyClass()
    {
        // list could be null
        foreach(IntPtr i in list)
        {
            CloseHandle(i);
        }
    }
}

If list is null when the finalizer runs, it will throw an unhandled NullReferenceException, which will terminate the program. To fix this error, you need to protect the finalizer like this:

~MyClass()
{
    // list could be null
    if (list != null)
    {
        foreach(IntPtr i in list)
        {
            CloseHandle(i);
        }
    }
}

Finalizers should not raise any unhandled exceptions, except in very system critical conditions, such as OutOfMemory. As of .NET 2.0, throwing an unhandled exception will terminate the entire process.

Finalizers should be simple enough so they don't fail. Since memory allocations may fail due to a lack of memory (we are, after all, running in the context of the GC), you should never allocate memory from within a finalizer. This is especially important in a critical finalizer or from SafeHandle's ReleaseHandle method. Critical finalizers are restricted to only calling a reliable subset of the Framework. If your critical finalizer detects corruption, or gets a bad error code from the Win32 subsystem, throwing an exception may be a reasonable way of reporting this error, though for SafeHandle's ReleaseHandle, you should return false.

In some very rare circumstances, only critical finalizers will be run. In even rarer circumstances, no finalizers will be run. You should never assume that your finalizer will always run. A good way to handle this is to wrap critical resources in a non-public instance that has a finalizer. This way, anyone that uses your type won't be able to suppress finalization. If you can migrate to using SafeHandle and never expose it outside your class, you can guarantee finalization of your resources.

If you need a finalizer that absolutely must execute, consider using a critical finalizable object. SafeHandle is one such object. Any class that inherits from CriticalFinalizerObject is also safe.

Except in very controlled designs, such as the Dispose(bool disposing) method, you should not call virtual members from within your finalizer. The most derived implementation of the virtual method will be run, and there is no guarantee that it will chain to its base class as it should.

Finalizers can be run in any order, on any thread, can occur on multiple objects simultaneously, and even on the same object simultaneously. In general, there are no guarantees that the threading policy for finalization will stay the same in the future, so you should not depend on how it is implemented today. Your finalizer should be able to run on any thread; that is, it should be thread-safe and thread agnostic.

Advanced Information

For a full description of the threading environment for finalization, you can see the blog entry by Chris Brumme at MSDN[^].

In addition to being thread agnostic and thread safe, your finalizer should avoid making any blocking calls. You should not perform any synchronization or lock acquisition, sleep the thread, or any other similar operations, unless there is a real security or stress bug that would cause finalization to fail. Blocking execution in the finalizer could delay or even prevent other finalizers in the queue from running. If you have to execute atomic thread-safe operations, you should use the Interlocked class since it is lightweight and non-blocking.

It is important to remember not to modify the thread context from within your finalizer, as it will end up polluting the finalizer thread. The finalizer will run an entirely separate thread than the one your object was on when it was alive, so don't leave the finalizer thread impersonated, access thread local storage, or change the thread's culture.

If you need to recycle (resurrect) the object, you should try to do it in your Dispose method. Only do this in the finalizer as a last resort. Re-registering an object has performance implications, and can cause unexpected behavior. In addition, you should never assume that by avoiding resurrecting your object, you have prevented it from being accessed after (or during) finalization. It is possible that other objects could attempt to use your objects as if you were still alive. The best option is to throw an exception in these situations, treating it as a similar situation to accessing an already disposed object.

Value types should not have a finalizer. Only reference types get finalized by the runtime.

Syntax Note

For C# developers, the C# destructor syntax is automatically converted to a Finalize() method by the compiler. If you implement the C# destructor syntax, be sure not to include a call to base.Finalize() since it will be included for you.

For example, the following C# class:

public class MyClass
{
   public MyClass()
   {
   }

   ~MyClass()
   {
      // object cleanup
   }
}

is automatically compiled to:

public class MyClass
{
   public MyClass()
   {
   }

   protected void Finalize()
   {
      try
      {
         // object cleanup
      }
      finally
      {
         base.Finalize();
      }
   }
}

If you try to define both a destructor and a Finalize() method, or you try to explicitly call your base class' Finalize() method, the compiler will generate an error. The compiler will automatically determine the presence of destructors in a class hierarchy and call every destructor in the proper order (from the lowest subclass to the topmost base class).

Using Disposable Objects

Implementing IDisposable and the Dispose Pattern are very important for how you write a class, but do not dictate how you use an instance of the class. Unfortunately, there is no way to force the users of your class to call Dispose when they are done using it, or to force them to use proper exception handling to make sure that Dispose is called even when an exception is thrown.

If the object you are using implements IDisposable, or even just implements a public Dispose method, the client should properly scope the code and then dispose of the resources it holds in a try/finally block. Without the try/finally block, the client's call to Dispose will never be reached if the calling methods cause an exception to be thrown. You should never ignore exceptions that are thrown from a Dispose call.

The using Statement

The C# and VB languages provide the using statement to make it easier for developers to work with disposable objects by automatically disposing of them when control leaves that scope. C++ provides this through the stack allocation semantics.

For example, the following C# code:

public void DoWork()
{
    using (MyClass myClass = new MyClass())
    {
        myClass.SomeMethod();
    }
}

is equivalent to the following C++ code:

public void DoWork()
{
    MyClass myClass;
    myClass.SomeMethod();
}

This is automatically translated by the compiler to:

public class Test
{
    public void DoWork()
    {
        MyClass myClass = new MyClass();

        try
        {
            myClass.SomeMethod();
        }
        finally
        {
            if (myClass != null)
            {
                IDisposable disposable = myClass;
                disposable.Dispose();
            }
        }
     }
}

As you can see, the using statement considerably simplifies the code, which makes it much more attractive from the developer's point of view.

There are many different variations on the syntax of the using statement, and you are allowed to "nest" or "stack" statements.

The following examples are all equivalent:

public void DoWork()
{
    using (MyClass myClass = new MyClass(), myClass2 = new MyClass())
    {
        myClass.SomeMethod();
        myClass2.SomeMethod();
    }
}

public void DoWork()
{
    using (MyClass myClass = new MyClass())
    using (MyClass myClass2 = new MyClass())
    {
        myClass.SomeMethod();
        myClass2.SomeMethod();
    }
}

which is equivalent to this C++ code:

public void DoWork()
{
    MyClass myClass;
    MyClass myClass2;

    myClass.SomeMethod();
    myClass2.SomeMethod();
}

You can also stack using statements when using different disposable types. The following examples show this and are equivalent:

public void DoWork()
{
    WebRequest request = WebRequest.Create("http://www.codeproject.com");
    using (WebResponse response = request.GetResponse())
    {
        using (Stream stream = response.GetResponseStream())
        {
            using (StreamReader reader = new StreamReader(stream))
            {
                string result = reader.ReadToEnd().Trim();
            }
        }
    }
 }

public void DoWork()
{
    WebRequest request = WebRequest.Create("http://www.codeproject.com");
    using (WebResponse response = request.GetResponse())
    using (Stream stream = response.GetResponseStream())
    using (StreamReader reader = new StreamReader(stream))
    {
        string result = reader.ReadToEnd().Trim();
    }
}

Interfaces and the using Statement

As we have seen, the using statement generates a type-safe implicit cast to IDisposable. Unfortunately, this prevents the using statement from being used with interfaces, even if the implementing type supports IDisposable.

For example, the following code will not compile:

public interface ISomeInterface
{
    void SomeMethod();
}

public class MyClass : ISomeInterface, IDisposable
{
   public void SomeMethod()
   {
   }

   public void Dispose()
   {
   }
}

public class Test
{
    public void DoWork()
    {
        using (ISomeInterface myClass = new MyClass())
        {
            myClass.SomeMethod();
        }
    }
}

One possible workaround for this is to make ISomeInterface derive from IDisposable instead of MyClass. This may not be possible in all cases, and may not be desirable, depending on where ISomeInterface is defined and what its purpose is.

A Complete Example

Here is a complete (and slightly more complex) example that implements the Dispose Pattern in C#.

using System;
using System.Security;
using System.ComponentModel;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;

public class ComplexWindow : IDisposable
{
    private MySafeHandleSubclass handle; // pointer for a resource
    private Component component; // other resource you use
    private bool disposed = false;

    public ComplexWindow()
    {
        handle = CreateWindow("MyClass", "Test Window",
            0, 50, 50, 500, 900,
            IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
        component = new Component();
    }

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

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            // if this is a dispose call dispose on all state you
            // hold, and take yourself off the Finalization queue.
            if (disposing)
            {
                if (handle != null)
                {
                    handle.Dispose();
                }

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

            // free your own state (unmanaged objects)
            AdditionalCleanup();

            this.disposed = true;
        }
    }

    // finalizer simply calls Dispose(false)
    ~ComplexWindow()
    {
        Dispose(false);
    }

    // some custom cleanup logic
    private void AdditionalCleanup()
    {
        // this method should not allocate or take locks, unless
        // absolutely needed for security or correctness reasons.
        // since it is called during finalization, it is subject to
        // all of the restrictions on finalizers above.
    }

    // whenever you do something with this class, check to see if the
    // state is disposed, if so, throw this exception.
    public void ShowWindow()
    {
        if (this.disposed)
        {
            throw new ObjectDisposedException("");
        }
        // otherwise, do work
    }

    [DllImport("user32.dll", SetLastError = true,
        CharSet = CharSet.Auto, BestFitMapping = false)]
    private static extern MySafeHandleSubclass CreateWindow(
        string lpClassName, string lpWindowName, int dwStyle,
        int x, int y, int nWidth, int nHeight, IntPtr hwndParent,
        IntPtr Menu, IntPtr hInstance, IntPtr lpParam);

    internal sealed class SafeMyResourceHandle : SafeHandle
    {
        private HandleRef href;

        // called by P/Invoke when returning SafeHandles
        private SafeMyResourceHandle () : base(IntPtr.Zero, true)
        {
        }

        // no need to provide a finalizer - SafeHandle's critical
        // finalizer will call ReleaseHandle for you.
        public override bool IsInvalid
        {
            get
            {
                return handle == IntPtr.Zero;
            }
        }

        override protected bool ReleaseHandle()
        {
            // this method is a constrained execution region, and
            // *must* not allocate
            return DeleteObject(href);
        }

        [DllImport("gdi32.dll", SuppressUnmanagedCodeSecurity]
        [ReliabilityContract(Consistency.WillNotCorruptState,
            CER.Success)]
        private static extern bool DeleteObject(HandleRef hObject);

        [DllImport("kernel32")]
        internal static extern SafeMyResourceHandle CreateHandle(
            int someState);
    }
}

// derived class
public class MyComplexWindow : ComplexWindow
{
    private Component myComponent; // other resource you use
    private bool disposed = false;

    public MyComplexWindow()
    {
        myComponent = new Component();
    }

    protected override void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                // free any disposable resources you own
                if (myComponent != null)
                {
                    myComponent.Dispose();
                }
                this.disposed = true;

            }

            // perform any custom clean-up operations
            // such as flushing the stream
        }

        base.Dispose(disposing);
    }
}

Conclusion

As you can see, there are a lot of details involved in properly implementing IDisposable and the Dispose Pattern. However, by following the proper template and rules, you can ensure that your disposable objects are handled correctly and behave as first-class citizens in the garbage collection process.

References and Further Reading

For further information, you can check out the following references:

Revision History

13-December-2006:

  • Added links to the MSDN articles by Jeffery Richter.

1-September-2006:

  • Major rewrite based on the guidelines presented by the Framework Design Guidelines.
  • Revised based on comments.
  • Revised the References section.

27-August-2006:

  • Original article.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here