Sunday, August 8, 2010

IDisposable, what is it, when to use it, and how to use it

External References
Common Misconceptions
  • It means objects are Garbage Collected more quickly. (Incorrect).
  • Its a good way to make sure all your references are set to null. (Not necessary).
  • Its good practice to implement all the time. (Incorrect).
  • The Garbage Collector calls IDisposable.Dispose() automatically. (Incorrect).
  • Its a good idea when you are creating lots of objects in a short period of time.  (There is a better way).

What is it?
The IDisposable interface seems to be a commonly misunderstood and misused interface.  Some developers like to implement it as a matter of course thinking it is good practice.  But it is absolutely not necessary when dealing with managed objects (native .Net objects).  In .Net the Garbage Collector (GC) is more than capable of finding and disposing all objects quickly and efficiently.  

To quote Andrew Troelsen: "Allocate an object onto the heap using the new keyword and forget about it".

The GC stores lists of objects in generations "young" objects have a lower generation number than longer lived objects.  The lists of young generation objects are examined more frequently than higher generation lists of objects.  Commonly there are many more short lived objects than long lived ones.  The IDisposable interface is more than an interface, you could argue its more of a design pattern. To use it correctly requires more than just merely implementing the interface and satisfying the compiler.

When to use it?
The interface is only necessary to properly clean up resources and release memory when a class references unmanaged code.  Commonly this boils down to:
  • When a class references or calls a COM object.
  • When a class references or calls a C++ object, (or Win32).
  • When a class has an OS File Handle to a file resource that needs to be closed when the object is destroyed.
  • You inherit from a class that implements IDisposable.
  • You have a long lived field reference to an IDisposable class.
Just to reiterate, you do not need to implement it when only referencing .Net native types (managed types) or types that you have created yourself that are based on other .Net types etc.  You also do not need to implement it if one of the classes you reference has a reference to unmanaged code, only worry about the code in the current class (OO design principle "encapsulation").  

You also do not need to set objects to null; you may have once upon a time in COM and preceding technologies, but not in .Net.  By doing so you are merely adding an unnecessary assignment to your code which if used everywhere will ultimately slow down your application.  

So, how to use it properly?
REMEMBER: If you do choose to implement IDisposable you are declaring to any consumers of your class that its life cycle needs to be carefully managed. 99% of the time it should be used within a using block.  It should never be returned as a result of a function.  If you consume a class that implements IDisposable, then ideally you need enclose it in a using block and not keep a field reference.  If you need to keep a field reference, then your class should also implement IDisposable. In addition if you inherit from a class that implements IDisposable then your new class also needs to consider reimplementing IDisposable.

Best shown with an example. Here's an example of how to implement the interface correctly.  

// Design pattern for the base class. 
// By implementing IDisposable, you are announcing that instances 
// of this type allocate scarce resources. 
public class BaseResource: IDisposable { 
    // Pointer to an external unmanaged resource. 
    private IntPtr handle; 
    
    // Other managed resource this class uses. 
    private Component Components; 
    
    // Track whether Dispose has been called. 
    private bool disposed = false; 
    
    // Constructor for the BaseResource object. 
    public BaseResource() { 
        // Insert appropriate constructor code here. 
    } 
    
    /// <summary>
    /// Implement IDisposable. 
    /// Do not make this method virtual. 
    /// A derived class should not be able to override this method
    /// </summary> 
    public void Dispose() { 
        Dispose(true); 
        
        // Take yourself off the Finalization queue 
        // to prevent finalization code for this object 
        // from executing a second time. 
        GC.SuppressFinalize(this); 
    } 
    
    /// <summary>
    /// Dispose(bool disposing) executes 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.
    /// </summary>
    /// <param name="disposing"><c>true</c> to release both managed and unmanaged
    /// resources; <c>false</c> to release only unmanaged resources.</param>
    protected virtual void Dispose(bool disposing) { 
        // Check to see if Dispose has already been called. 
        if(!this.disposed) { 
            // If disposing equals true, dispose all managed 
            // and unmanaged resources. 
            if(disposing) { 
                // Dispose managed resources. 
                Components.Dispose(); 
            } 
            
            // Release unmanaged resources. If disposing is false, 
            // only the following code is executed. 
            CloseHandle(handle); 
            handle = IntPtr.Zero; 
            
            // Note that this is not thread safe. 
            // Another thread could start disposing the object 
            // after the managed resources are disposed, 
            // but before the disposed flag is set to true. 
            // If thread safety is necessary, it must be 
            // implemented by the client. 
        } 
        
        disposed = true; 
    } 
    
    /// <summary>
    /// Finalizes an instance of the <see cref="BaseResource"/> class.
    /// Use C# destructor syntax for finalization code. 
    /// This destructor will run only if the Dispose method 
    /// does not get called. 
    /// It gives your base class the opportunity to finalize. 
    /// Do not provide destructors in types derived from this class.
    /// </summary> 
    ~BaseResource() { 
        // Do not re-create Dispose clean-up code here. 
        // Calling Dispose(false) is optimal in terms of 
        // readability and maintainability. 
        Dispose(false); 
    } 
    
    // Allow your Dispose method to be called multiple times, 
    // but throw an exception if the object has been disposed. 
    // Whenever you do something with this class, 
    // check to see if it has been disposed. 
    public void DoSomething() { 
        if(this.disposed) { 
            throw new ObjectDisposedException(); 
        } 
    } 
} 

// Design pattern for a derived class. 
// Note that this derived class inherently implements the 
// IDisposable interface because it is implemented in the base class. 
public class MyResourceWrapper: BaseResource { 
    // A managed resource that you add in this derived class. 
    private ManagedResource addedManaged; 
    
    // A native unmanaged resource that you add in this derived class. 
    private NativeResource addedNative; 
    private bool disposed = false; 
    
    // Constructor for this object. 
    public MyResourceWrapper() { 
        // Insert appropriate constructor code here. 
    } 
    
    protected override void Dispose(bool disposing) { 
        if(!this.disposed) { 
            try { 
                if(disposing) { 
                    // Release the managed resources you added in 
                    // this derived class here. 
                    addedManaged.Dispose(); 
                } 
                
                // Release the native unmanaged resources you added 
                // in this derived class here. 
                CloseHandle(addedNative); 
                this.disposed = true; 
            } finally { 
                // Call Dispose on your base class. 
                base.Dispose(disposing); 
            } 
        } 
    } 
} 

// This derived class does not have a Finalize method 
// or a Dispose method without parameters because it inherits 
// them from the base class.


Its also important to note that there are a few best practice guidelines for IDisposable (from the Framework Design Guidelines book).
When a class implements IDisposable, it is declaring to the outside world that you must use it with care.  You must use it and it is your responsibility to call Dispose, only you will call Dispose it doesn't auto-magically get called.  Generally most experienced senior developers I know will agree you should always use a IDisposable object wrapped in a Using block.

Secondly you avoid returning an IDisposable object as the result of a function, the consumer more than likely will not check to see if it needs to be disposed; leading to IDisposable.Dispose() not being called. Hello memory leak.  This follows the first rule above, you cannot wrap an IDisposable object in a Using block and return it from a function.

What if you are creating lots of objects in a short period of time and only need them very briefly?
If doing nothing means there will be a lot of memory locked for a time until the GC collects them, then simply call GC.Collect() every so often periodically to kick the collector into checking for resources to free immediately.  Alternatively you can use the GC.AddMemoryPressure() and GC.RemoveMemoryPressure() functions to tell the GC there is a higher demand on memory than is usual and it should consider collection more frequently. These two options are  better than implementing IDisposable, because its less work, and by implementing IDisposable your code has more work to do because you are running another method on each of the large number of objects you have created and will more than likely be slower.

Be careful with these methods however, as with anything like this you should only use these if you have already proved with testing that it is better to use them than to not.

As a side issue, when calling GC.Collect() you should also call GC.WaitForPendingFinalizers().
When you manually force a collection, you should call WaitForPendingFinalizers() to cause the current thread to wait for all finalizers to finish running. This prevents you from calling any code on an object while it is being destroyed. Once this method returns (synchronously) you can check for null reference as per normal and then safely invoke a method if the pointer is non-null.

So how do you stop IDisposable from spreading through your code like a cancer?
There's a good topic on Stack Overflow on this very topic here. The short answer is its not easy. The long and short of it is:

  • Only implement IDisposable if you are using unmanaged resources or keeping a reference to an IDIsposable and your class is responsible for creating it. (The class creating it should be the class responsible for destroying it).
  • Prefer to consume IDisposable classes inside a using block.
  • Avoid returning an IDisposable class as a return object.
  • Keep expensive resource management in one place.
  • Prefer a create-on-demand pattern over singletons or long lived IDisposables.


Cheers

1 comment: