About Me

Hello, I'm an eclectic software professional working with enterprise software at SAP. This blog only contains my personal views, thoughts and opinions. It is not endorsed by SAP nor does it constitute any official communication of SAP.

Sunday, January 31, 2010

Revisiting IDisposable

In my last post, I've introduced a better alternative to the official pattern for implementing the IDisposable interface in .NET:

public class BetterDisposableClass : IDisposable {

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

  protected virtual void CleanUpManagedResources() { 
    // ...
  }
  protected virtual void CleanUpNativeResources() {
    // ...
  }

  ~BetterDisposableClass() {
    CleanUpNativeResources();
  }

}

This pattern is easier to understand because it cleanly separates the responsibilities to implement a disposable class that releases unmanaged (native) and managed resources in different methods. Each method is highly focused on a single identifiable task.

This concept can be taken one step further, where different classes are created to deal with each concern. First, each unmanaged resource must be encapsulated in its own class:

public class NativeDisposable : IDisposable {

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

  protected virtual void CleanUpNativeResource() {
    // ...
  }

  ~NativeDisposable() {
    CleanUpNativeResource();
  }

  // ...

  IntPtr _nativeResource;

}

This class encapsulates a single unmanaged resource, and needs to implement the finalizer as a last resort to release it, since unmanaged resources are not claimed by the garbage collector. All classes that need to deal with a particular unmanaged resource will do so through its encapsulating class. The result is that they won't need to be concerned with unmanaged resources anymore.

And so, classes that are composed of other disposable objects should also implement IDisposable to release them, in a very straightforward way:

public class ManagedDisposable : IDisposable {

  // ...

  public virtual void Dispose() {
    _otherDisposable.Dispose();
  }

  IDisposable _otherDisposable;

}

Now Dispose is not ambiguous like in the original pattern, since it only deals with managed resources. This class is much easier to implement, as it doesn't need a finalizer and the logic to suppress it should Dispose be called.

This design properly separates the responsibilities to implement IDisposable and results in a much more robust and maintainable solution.

4 comments:

  1. Great post! I really like the simplicity that results from this usage pattern. Thanks for sharing.

    ReplyDelete
  2. Thank you. Great post!

    ReplyDelete
  3. Many thanks for this. I found it useful to implement as an abstract class:-

    public abstract class myDisposable : IDisposable
    {
    public void Dispose()
    {
    disposeManaged();
    disposeNative();
    GC.SuppressFinalize(this);
    }
    //---------------------------------------------------------------------------------
    protected abstract void disposeManaged();
    protected virtual void disposeNative() {}
    //---------------------------------------------------------------------------------
    ~myDisposable()
    {
    disposeNative();
    }
    }

    ReplyDelete
  4. Yes Grebe, that's a very good idea.

    ReplyDelete