Thursday, 3 July 2008

Implementing IDisposable

In general, while using C# (or any .NET language for that matter), you do not need to worry about resource disposal and memory management. The main exception, however, is when you are using an unmanaged resource – like a database connection or file – or any other resource, except for memory. The .NET framework provides the IDisposable interface for this purpose. Since there is no way to guarantee that the Dispose() method gets called, it is considered a best practice to wrap a call to the Dispose() method in an objects' finalizer (destructor).

When the garbage collector runs, all objects with a finalizer are left in memory and have their finalizers executed. Objects without finalizers are simply deleted from memory – which is where memory and resource leaks can potentially happen.

The IDisposable interface is defined as follows:

   1:  public interface IDisposable
   2:  {
   3:       void Dispose();
   4:  }

It contains nothing more than a single method called Dispose().

The following class declaration show how to implement the interface:

   1:  public class MyClass : IDisposable
   2:  {
   3:       private bool _isDisposed = false;
   4:   
   5:       ~MyClass()
   6:       {
   7:            Dispose(false);
   8:       }
   9:   
  10:       public void Dispose()
  11:       {
  12:            Dispose(true);
  13:            GC.SuppressFinalize(true);
  14:       }
  15:   
  16:       protected virtual void Dispose(bool isDisposing)
  17:      {
  18:            if (_isDisposed)
  19:                 return;
  20:            if (isDisposing)
  21:           {
  22:                // Free managed resources
  23:            }
  24:   
  25:           // Free unmanaged resources here
  26:          _isDisposed = true;
  27:      }
  28:  }


The overloaded virtual Dispose() method in the code above is used when the class is used as a base class, and allows for any derived classes to call the base class disposal method, thus making sure that all resources are properly released.

The most important thing to remember when implementing disposal is that you should only be freeing resources in the Dispose() method. Do not call any object methods or create references to the current object or do anything else that could effectively resurrect that object. Remember, you never know in what order objects will be disposed, so you may end up working on an object that is already disposed or finalized. Doing that puts the object in question back into a global list of objects, but the garbage collector will not know to dispose of it again because it’s already been done!

In general, I think it is best practice to call an objects' Dispose() method, (or similar - i.e. Close()) when the object is no longer required to allow resources to be returned to the system. Often an object with a Close() method for example will call Dispose() from 'behind the scenes'.

0 comments: