DekGenius.com
[ Team LiB ] Previous Section Next Section

Recipe 3.36 Disposing of Unmanaged Resources

Problem

Your class references unmanaged resources such as some type of handle, or it manipulates a block of memory or a file via P/Invoke methods or your class uses a COM object that requires some cleanup method to be called before it is released. You need to make sure that the resources are released properly and in a timely manner. In a garbage-collected environment, such as that used by the Common Language Run-time (CLR), you cannot assume either will happen.

Solution

Implement the dispose design pattern, which is specific to .NET. The class that contains a reference to the unmanaged resources is shown here as Foo. This object contains references to a COM object called SomeCOMObj, a FileStream object called FStream, and an ArrayList that may or may not contain references to unmanaged resources. The source code is:

using System;
using System.Collections;
using System.IO;

[DllImport("Kernel32.dll", SetLastError = true)]
private static extern IntPtr CreateSemaphore(IntPtr lpSemaphoreAttributes, 
        int lInitialCount, int lMaximumCount, string lpName);

[DllImport("Kernel32.dll", SetLastError = true)]
private static extern bool ReleaseSemaphore(IntPtr hSemaphore, int lReleaseCount, 
        out IntPtr lpPreviousCount);

public class Foo : IDisposable
{
    public Foo( ) {}

    // Replace SomeCOMObj with your COM object type
    private SomeCOMObj comObj = new SomeCOMObj( );
    private FileStream fileStream = new FileStream(@"c:\test.txt", 
      FileMode.OpenOrCreate);
    private ArrayList aList = new ArrayList( );
    private bool hasBeenDisposed = false;
    private IntPtr hSemaphore = IntPtr.Zero;   // Unmanaged handle

    // Protect these members from being used on a disposed object
    public void WriteToFile(string text)
    {
        if(hasBeenDisposed)
        {
            throw (new ObjectDisposedException(this.ToString( ), 
                                               "Object has been disposed"));
        }

        UnicodeEncoding enc = new UnicodeEncoding( );
        fileStream.Write(enc.GetBytes(text), 0, text.Length);
    }

    public void UseCOMObj( )
    {
        if(hasBeenDisposed)
        {
            throw (new ObjectDisposedException(this.ToString( ), 
                                               "Object has been disposed"));
        }

        Console.WriteLine("GUID: " + comObj.GetType( ).GUID);
    }

    public void AddToList(object obj)
    {
        if(hasBeenDisposed)
        {
            throw (new ObjectDisposedException(this.ToString( ), 
                                               "Object has been disposed"));
        }

        aList.Add(obj);
    }

    public void CreateSemaphore( )
    {
        // Create unmanaged handle here
        hSemaphore = CreateSemaphore(IntPtr.Zero, 5, 5, null);
    }

    // The Dispose methods
    public void Dispose( )
    {
        Dispose(true);
    }

    protected virtual void Dispose(bool disposeManagedObjs)
    {
        if (!hasBeenDisposed)
        {
            if (disposeManagedObjs)
            {
                // Dispose all items in an array or ArrayList
                foreach (object obj in aList)
                {
                    IDisposable disposableObj = obj as IDisposable;
                    if (disposableObj != null)
                    {
                        disposableObj.Dispose( );
                    }
                }

                // Dispose managed objects implementing IDisposable
                fileStream.Close( );

                // Reduce reference count on RCW
                while (Marshal.ReleaseComObject(comObj) > 0);

                GC.SuppressFinalize(this);
            }
            // Release unmanaged handle here
            IntPtr prevCnt = new IntPtr( );
            ReleaseSemaphore(hSemaphore, 1, out prevCnt);

            hasBeenDisposed = true;
        }
    }

    // The destructor
    ~Foo( )
    {
        Dispose(false);
    }

    // Optional Close method
    public void Close( )
    {
        Dispose( );
    }
}

The following class inherits from Foo:

// Class inherits from an IDisposable class
public class Bar : Foo
{
    //...

    private bool hasBeenDisposed = false;

    protected override void Dispose(bool disposeManagedObjs)
    {
        if (!hasBeenDisposed)
        {
            try
            {
                if(disposeManagedObjs)
                {
                    // Call Dispose/Close/Clear on any managed objects here...        
                }

                // Release any unmanaged objects here...
            }
            finally
            {
                // Call base class' Dispose method
                base.Dispose(disposeManagedObjs);
                hasBeenDisposed = true;
            }
        }
    }
}

Whether this class directly contains any references to unmanaged resources, it should be disposed of as shown in the code.

Discussion

The dispose design pattern allows any unmanaged resources held by an object to be cleaned up from within the managed environment. This pattern is flexible enough to allow unmanaged resources held by the disposable object to be cleaned up explicitly (by calling the Dispose method) or implicitly (by waiting for the garbage collector to call the destructor). Finalizers are a safety net to clean up objects when you forget to do it.

This design pattern should be used on any base class that has derived types that hold unmanaged resources. This indicates to the inheritor that this design pattern should be implemented in their derived class as well.


All the code that needs to be written for a disposable object is written within the class itself. First, all disposable types must implement the IDisposable interface. This interface contains a single method, Dispose, which accepts no parameters and returns void. The Dispose method is overloaded to accept a Boolean flag indicating whether any managed objects referenced by this object should also be disposed. If this parameter is true, managed objects referenced by this object will have their Dispose method called, and unmanaged resources are released; otherwise, only unmanaged resources are released.

The IDisposable.Dispose method will forward its call to the overloaded Dispose method that accepts a Boolean flag. This flag will be set to true to allow all managed objects to attempt to dispose of themselves as well as to release unmanaged resources held by this object.

The IDisposable interface is very important to implement. This interface allows the using statement to take advantage of the dispose pattern. A using statement that operates on the Foo object is written as follows:

using (Foo f = new Foo( ))
{
    f.WriteToFile("text");
}

Always implement the IDisposable interface on types that contain resources that need to be disposed or otherwise explicitly closed or released. This allows the use of the using keyword and aids in self-documenting the type.


A foreach loop will also make use of the IDisposable interface, but in a slightly different manner. After each iteration of this loop, the Dispose method is called via the enumerator type of the object being enumerated. The enumerator type is usually a nested class that implements IEnumerator, and, in this case, would also implement IDisposable. The foreach loop guarantees that it will call the Dispose method on the enumerator object to allow each individually enumerated object to be disposed of properly.

The overloaded Dispose method that accepts a Boolean flag contains a static method call to GC.SuppressFinalize to force the garbage collector to remove this object from the fqueue, or finalization queue. The fqueue allows the garbage collector to run C# destructors at a point after the object has been freed. However, this ability comes at a price: it takes many garbage collection cycles to completely collect an object with a destructor. If the object is placed on the fqueue in generation 0, the object will have to wait until generation 1 is collected, which could be some time, since it usually takes 10 generation 0 collections before generation 1 is collected. The GC.SuppressFinalize method prevents the need for the object to stay in memory for all of these garbage collection cycles. Calling this static method from within the Dispose method is critical to writing better performing classes.

Always call the GC.SuppressFinalize method in the base class Dispose method. Doing so will allow your object to be taken off of the finalization queue in the garbage collector allowing for earlier collection. This will help prevent memory retention and will help your application's performance.


A destructor is also added to this class. The destructor contains code to call the overloaded Dispose method, passing in false as its only argument. Note that all cleanup code should exist within the overloaded Dispose method that accepts a Boolean flag. All other methods should call this method to perform any necessary cleanup. The destructor will pass a false value into the Dispose method to prevent any managed objects from being disposed. Remember, the destructors run in their own thread. Attempting to dispose of objects that may have already been collected or are about to be collected could have serious consequences for your code, such as resurrecting an object into an undefined state. It is best to prevent any references to other objects while the destructor is running.

It is possible to add a Close or even a Clear method to your class to be called as well as the Dispose method. Several classes in the FCL use a Close or Clear method to clean up unmanaged resources:

FileStream.Close( )
StreamWriter.Close( )
TcpClient.Close( )
MessageQueue.Close( )
SymmetricAlgorithm.Clear( )
AsymmetricAlgorithm.Clear( )
CryptoAPITransform.Clear( )
CryptoStream.Clear( )

Each of these classes also contains a Dispose method. The Clear method usually calls the Dispose method directly. There is a problem with this design. The Clear method is used extensively throughout the FCL for classes such as ArrayList, Hashtable, and other collection type classes. However, the Clear method of the collection classes performs a much different task; instead of calling the IDisposable.Dispose method, it clears the collection of all its items. This Clear method has nothing to do with releasing unmanaged resources or calling the Dispose method.

Another problem is the confusion with the Close, Clear, and Dispose methods of the CryptoStream class. The Close method simply flushes the pending data and attempts to close the underlying stream object. The Clear method forwards its call on to the Dispose method. The Dispose method cleans up this object, but does not close the underlying stream object. If you look at the base Stream class, it has an implementation of IDisposable, and it will close the stream when you dispose it. But CryptoStream replaces this implementation with its own that fails to close the stream, and which doesn't call back into the Stream base class's Dispose implementation. So it's entirely inconsistent. From this, we can conclude that to completely clean up a CryptoStream object, we must first call Close and then either call Clear or Dispose. When in doubt, always default to calling the Dispose method on an object.

Consider not implementing a Close method unless it will be obvious to the user or inheritor of this class what it is for, or if you are deriving from a type such as Stream, which does not give you a choice, since the Stream class contains no implementation for this method. Never implement a Clear method that will be used to dispose your object. Instead, use the commonly recognized Dispose method. Otherwise, your code will not operate in a consistent manner with the disposable classes within the FCL.


This implementation does not follow the dispose design pattern. To follow this pattern, the Close method should simply forward its call on to the IDisposable.Dispose method. In addition, the Clear method is never mentioned in the dispose design pattern, so avoid using a Clear method for anything other than to remove elements from a collection type. As a note, this inappropriate usage of the Clear method is unique to the cryptography classes. All other classes in the FCL seem to use it correctly.

The overloaded Dispose method that accepts a Boolean flag will contain all of the logic to release unmanaged resources from this object as well as possibly calling Dispose on types referenced by this object. In addition to these two actions, this method can also reduce the reference count on any COM objects that are referenced by this object. The static Marshal.ReleaseComObject method will decrement the reference count by one on the COM object reference passed in to this method:

Marshal.ReleaseComObject(comObj);

To force the reference count to go to zero, allowing the COM object to be released and its Runtime Callable Wrapper (RCW) to be garbage collected, you could write the following code:

while (Marshal.ReleaseComObject(comObj) > 0);

Take great care when forcing the reference count to zero in this manner. If another object is using this COM object, the COM object will be released out from under this other object. This can easily destabilize a system. For more information on using this method, see Recipe 3.30.

Any callable method/property/indexer (basically, any nonprivate method except for the Dispose and Close methods and the constructor(s) and the destructor) should throw the ObjectDisposedException exception if it is called after the object has been disposed—that is, after its Dispose method has been called. A private field called hasBeenDisposed is used as a Boolean flag to indicate whether this object has been disposed; a true confirms that it has been disposed. This flag is checked to determine whether this object has been disposed at the beginning of every method/property/indexer. If it has been disposed, the ObjectDisposedException is thrown. This prevents the use of an object after it has been disposed and potentially placed in an unknown state.

Disposable objects should always check to see if they have been disposed in all of their public methods, properties, and indexers. If a client attempts to use your object after it has been disposed, an ObjectDisposedException should be thrown. Note that a Dispose method can be called multiple times after this object has been disposed without having any side effects (including the throwing of ObjectDisposedExceptions) on the object.


Any classes inheriting from Foo need not implement the IDisposable interface; it is implied from the base class. The inheriting class should implement the hasBeenDisposed Boolean flag field and use this flag in any methods/properties/indexers to confirm that this object has been disposed. Finally, a Dispose method is implemented that accepts a Boolean flag and overrides the same virtual method in the base class. This Dispose method does not have to call the GC.SuppressFinalize(this) static method; this is done in the base class's Dispose method.

The IDisposable.Dispose method should not be implemented in this class. When the Dispose method is called on an object of type Bar, the Foo.Dispose method will be called. The Foo.Dispose method will then call the overridden Bar.Dispose(bool) method, which, in turn, calls its base class Dispose(bool) method, Foo.Dispose(bool). The base class's destructor is also inherited by the Bar class.

All Dispose methods should call their base class's Dispose method.


If the client code fails to call the Dispose or Close method, the destructor will run and the Dispose(bool) method will still be called, albeit at a later time. The destructor is the object's last line of defense for releasing unmanaged resources.

See Also

See Recipe 3.29 and Recipe 3.30; see the "Dispose Interface," "Using foreach with Collections," and "Implementing Finalize and Dispose to Clean Up Unmanaged Resources" topics in the MSDN documentation.

    [ Team LiB ] Previous Section Next Section