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

About Dave Fancher

Dave Fancher is a Software Engineer at Performance Assessment Network in Carmel, Indiana, a Microsoft MVP for Visual F#, and author of The Book of F# from No Starch Press. He has been building software with the .NET Framework since version 1.1. Dave is active within the Indiana software development community as a member of IndySA, a speaker at user groups throughout the state, and a two-time contributor to Indy GiveCamp. When not writing code he enjoys spending time with his family, watching movies, photography, and gaming on on his Xbox One.

Posted on April 5, 2014, in C#, Languages, Software Development and tagged . Bookmark the permalink. 1 Comment.

Follow

Get every new post delivered to your Inbox.

Join 738 other followers

%d bloggers like this: