Another extension method: DisposeAfter

Ed previously blogged about our null-propagating extension method. This extension method is one example of a pattern that I find very interesting:

    public static ? InvokeOnSelf<T>(this T self, Func<T, ?> toInvoke)

Another interesting example of this pattern is DisposeAfter:

    public static T DisposeAfter<TDisposable, T>(this TDisposable d,
        Func<TDisposable, T> fn) where TDisposable : IDisposable

DisposeAfter is an extension method constrained to types which implement IDisposable. It lets you pass in work to be done before calling Dispose, and returns the result of that work.

Why is this useful?

Let’s look at a simple case of reading a row of data from an IDataReader:

    int id = 0;
    string name = null;
    using (IDataReader reader = command.ExecuteReader())
    {
        if (reader.Read())
        {
            id = reader.GetInt32(0);
            name = reader.GetString(1);
        }
    }
    // do some work with id and name

This is one case where using an anonymous type instance to encapsulate the read data could be useful. Doing so would enable us to know if data was read; we could simply return null when it wasn’t. It would also somewhat simplify the method by reducing the number of local variables.

There’s a big problem though: if we declare the anonymous type instance inside the using block, its scope will be constrained. But we cannot declare the anonymous type separately from its initialization.

We want to say something like:

    var result; // error CS0818: Implicitly-typed local variables must be

    initialized
    using (IDataReader reader = command.ExecuteReader())
    {
        if (reader.Read())
            result = new { Id = reader.GetInt32(0), Name = reader.GetString(1) };
    }
    // do some work with result

But that won’t compile.

Enter DisposeAfter:

    var result = command.ExecuteReader().DisposeAfter(r =>
        r.Read() ? new { Id = r.GetInt32(0), Name = r.GetString(1) } : null);
    // do some work with result

There, that’s nice! Since DisposeAfter is a generic method, the return value is strongly typed. And it doesn’t matter to the compiler that we’re returning an anonymous type instance; it can still infer the type of result.

Looking at the implementation of DisposeAfter we see that all of the passed in work is still done within a using statement, so objects will properly be disposed (even in the face of troublesome exceptions):

    public static T DisposeAfter<TDisposable, T>(this TDisposable d,
        Func<TDisposable, T> fn) where TDisposable : IDisposable
    {
        using (d)
            return fn(d);
    }

Let me know in the comments if you find this method useful. Hopefully you’ll also start to think about other circumstances where passing a delegate (or perhaps an expression tree) to an object via an extension method could be useful.

Posted by Jacob Carpenter on February 04, 2008