Sunday, December 30, 2007

Closures in C# 3.0

I've been hearing a lot about closures and continuations recently, possibly because people are starting to explore C# 3.0 a little more.  I wasn't all that hip to the terms, but they sure sounded smart, so I thought that I would explore a little bit.

Martin Fowler provides a very clear definition of a closure here.  Closures are essentially a block of code that can be passed as an argument to a function.  In addition, closures have access to any bound variables from the calling operation.  Here's an example of a very simple closure:

static void Main(string[] args)
{
    int x = 1;
    MyClosure(x, s => x++);
    Console.WriteLine(x);
}
 
public static void MyClosure(int number, Action<int> code)
{
    code(number);
}

You can see that the variable x is instantiated and assigned a value, and then used within the the block of code (in this case, it is incremented).

Obviously, you're likely not doing a whole lot of single increments of a number in your application, so we should try something a little more interesting.  So, let's create a generic filter extension method for a collection of objects.  I'll use my order item example from my Lambda Expressions post.  Here are the guts of my Filter extension method:

public static IEnumerable<T> Filter<T>(this IEnumerable<T> coll, Func<T, bool> predicate)
{
    foreach (T item in coll)
    {
        if (predicate(item))
            yield return item;
    }
}

If I need functionality that allows a user to see a list of products that is less than a certain threshold dollar amount, I can use a combination of this extension method and closures to do it.  Here's the code:

public static IEnumerable<OrderItem> ShowItemsThatCostLessThan(IEnumerable<OrderItem> items, double highPrice)
{
    return items.Filter(p => p.Price < highPrice);
}

In that example, you can see that the variable highPrice is passed into the method, and then used within the code block that does the comparison.

One thing that Fowler mentions in his post is that along with the formal requirement that a closure have access to the bindings of the environment they came from, there is an informal requirement as well:

The second difference is less of a defined formal difference, but is just as important, if not more so in practice. Languages that support closures allow you to define them with very little syntax. While this might not seem an important point, I believe it's crucial - it's the key to make it natural to use them frequently. Look at Lisp, Smalltalk, or Ruby code and you'll see closures all over the place - much more frequently used than the similar structures in other languages. The ability to bind to local variables is part of that, but I think the biggest reason is that the notation to use them is simple and clear.

The code definitely seems clear to me, it's the short part that I don't feel that great about.  Languages with rich collection objects like Ruby and Smalltalk make it much easier to give a confident yes to this argument.  See Fowler's other post about collections and closures.

I might just be splitting hairs there, and either way you can see how closures are formed, what they do, and can see all the potential they have for decreasing the amount of work you have to do.

References

Closure (computer science) - Wikipedia
Closure - Martin Fowler's Bliki

0 comments: