Revisiting the Using Function

A little over a year ago I wrote about replicating F#’s using function for use in C#. Since I wrote that piece I’ve reconsidered the approach a bit. At the time, my team wasn’t using static code analysis (I know, shame on us) so I didn’t consider that passing the IDisposable instance to the function directly can sometimes cause the static analysis to raise warning CA2000.

To recap the previous post on this subject, here’s the original version of the method:

public static TResult Using<TResource, TResult>(TResource resource, Func<TResource, TResult> action)
    where TResource : IDisposable
{
    using (resource) return action(resource);
}

With this approach, Using requires you to supply an IDisposable instance. When using a factory method such as Image.FromFile as shown next, the warning isn’t raised:

var dimensions =
    IDisposableHelper.Using(
        Image.FromFile(@"C:\Windows\Web\Screen\img100.png"),
        img => new Size(img.Width, img.Height));

Quite often, though, we create instances directly via the new operator. Consider reading the contents of a file with a StreamReader, like this:

var contents =
    IDisposableHelper.Using(
        new StreamReader(@"C:\dummy.txt"),
        reader => reader.ReadToEnd());

Creating an IDisposable instance with the new operator results in the CA2000 warning. We know that we’re disposing the StreamReader but it still fails the static analysis checks. We could suppress the warning but we can easily avoid it altogether by redefining the Using method as follows:

public static TResult Using<TResource, TResult>(Func<TResource> resourceFactory, Func<TResource, TResult> action)
    where TResource : IDisposable
{
    using (var resource = resourceFactory()) return action(resource);
}

Now, instead of accepting an IDisposable instance directly, Using accepts a factory function that returns the IDisposable instance. Then, inside the Using method, we invoke the factory function and assign the result to resource. All that remains is to update the offending code to reflect the signature change:

var contents =
    IDisposableHelper.Using(
        () => new StreamReader(@"C:\dummy.txt"),
        reader => reader.ReadToEnd());

This revised approach gives us another benefit – it defers creating the IDisposable instance until Using is executing and keeps it scoped to the actual using block within the method.

About these ads

One comment

Comments are closed.