Sunday, January 03, 2010

“Dispose” Of Duplicated Code

As you know, when working with disposable objects you always need to dispose them when you’re finished. The using block is great for this, but not always appropriate - sometimes you have one or more disposable objects as fields in a class (or being passed among classes) and you can’t wrap them in a using block.

In these situations I often end up finding myself writing code like this;

IDisposable dispObj = value as IDisposable;
if (dispObj != null)
{
try
{
dispObj.Dispose();
}
catch
{
#if DEBUG
throw;
#endif
}
}



First, I attempt to cast the object to the IDisposable interface, but I use the as keyword to ensure no exception is thrown if the object doesn’t support the interface.



Why ? Because sometimes the item I (may) need to dispose is a custom component loaded from another assembly which I’m accessing via a common interface or base class. In this case I don’t know at design time whether or not it needs to be disposed… but I can tell at runtime by checking if it supports the IDisposable interface. Also, some classes that DO need to be disposed don’t expose Dispose as a public method on their default interface and must be cast to IDisposable to access the Dispose method (i.e they implement IDisposable explicitly, not implicitly).



Next, assuming the cast was successful I setup an error handler that catches all exceptions… bad practice I know, but I do it (most of the time) anyway because failing to dispose an object isn’t usually something that should crash my application… normally the GC will ensure the object is properly disposed later, the user certainly doesn’t care the dispose failed, besides usually there is no ill effect from ignoring an error here. Errors here are highly uncommon and I might have a bunch of other objects that need to be disposed and won’t be if we throw an exception that isn’t handled here.



You’ll note however that I don’t ignore the exception if the code is in debug mode… this alerts me to any possible issues while I’m developing/testing the code in Visual Studio.



Finally, I dispose the object.



Repeating this all over the place seemed like a bad idea, so I came up with the following two methods in order to re-use the code. I have them as static methods on a static class, but you could make them instance or extension methods;



/// <summary>
/// Attempts to dispose an object. Determines first if the object is disposable, and not null, and if so then disposes it.
/// </summary>
/// <remarks>Any errors that occur during dispose are swallowed by this method.</remarks>
/// <param name="value">The object to be disposed.</param>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
public static void DisposeObject(object value)
{
    IDisposable dispObj = value as IDisposable;
    if (dispObj != null)
    {
        try
        {
            dispObj.Dispose();
        }
        catch
        {
            #if DEBUG
                throw;
            #endif
        }
    }
}


/// <summary>
/// Disposes all objects in the specified enumerable set, and disposes the set itself where the object(s) support <see cref="System.IDisposable"/>.
/// </summary>
/// <remarks>Any object not supporting <see cref="System.IDisposable"/> is ignored. If the enumerable set is null, then the function does nothing and returns. Any errors that occur during the dispose are swallowed.</remarks>
/// <param name="values">An enumerable set of objects to be disposed.</param>
public static void DisposeObjects(System.Collections.IEnumerable values)
{
    if (values != null)
    {
        foreach (object o in values)
        {
            DisposeObject(o);
        }
        DisposeObject(values);
    }
}









No comments:

Post a Comment