Stupid C# Tricks – Closures
- March 1st, 2010
- Posted in General Nerdery
- By AMB
- Write comment
So in my current quest to poke around in some of the more esoteric corners of C#, I’ve gotten interested in C#’s implementation of closures. Like most .NET languages, C# is trying really hard to be everything to everyone, and so as of language version 3.0 it has native support for closures. Now, many of you functional folks in the audience are probably either saying “woo-hoo closures! Yeah! ROCK!” or possibly “wait, people use C#? I thought everyone was on Ruby these days.”
The .NET folks are probably searching in vain for the MSDN article on this mythical “closure” thing.
So a closure (for those unfamiliar) is a chunk of code that can be passed around like a first-class object and which can reference variables in the namespace in which it was created. It’s essentially a method that’s treated like and object and can reference variables it could see at the time of its creation. These things are all the rage in Lisp- and Smalltalk-like languages. They’re one of the things that makes Lisp so powerful and they cause most Rubyists to get all wet in the trousers.
So what can closures do for you? Go go gadget example!
In this example, we’ll be creating a light-weight console app which determines the length of the Collatz Chain resulting from a given starting point. The Collatz Conjecture (unproven) states that the following function, if iterated, will eventually converge to 1, regardless of starting point:
f(x) = x/2 (X is even)
f(x) = 3x+1 (X is odd)
A Collatz Chain, then, is the sequence of results created by iteratively applying the above function starting from a particular number.
Now a recursive solution to this is pretty easy to bash out. There’s not much to the problem as stated here. But it serves as a pretty handy way to demonstrate closures. (The real mindfuck comes when you start examining the problem’s properties and the various interesting attempts at proving the conjecture. Also, the function makes for a really kickass graph and fractal.)
So, in the code below, I’m going to encapsulate the Collatz function in code block and pass it in to a wrapper class which will do that actual execution of the code. First, the wrapper:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace CollatzByClosures { public static class CollatzWrapper { public static Func method; public static int Execute() { return method(); } } } |
As you can see, there’s not a whole lot to this. The static CollatzWrapper class has a Function object that returns an integer, and it’s got a public Execute method that just returns the result of that method’s execution. Note that the method takes no arguments and that the wrapper is both static and contains no other member variables besides the function. “There’s nothing up my sleeve and nothing under my hat”, said the magician.
Next, the program that uses this wrapper:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace CollatzByClosures { class Program { private static int CollatzArgument = 0; private static int CollatzLength = 0; static void Main(string[] args) { if (args.Length > 0) { CollatzArgument = int.Parse(args[0]); } else { CollatzArgument = 42; } CollatzWrapper.method = ( () => { if(CollatzArgument%2 == 0) { CollatzLength++; return CollatzArgument/2; } else { CollatzLength++; return (CollatzArgument*3+1); } }); while (CollatzArgument > 1) CollatzArgument = CollatzWrapper.Execute(); if (args.Length > 0) { Console.WriteLine(args[0] + " results in a Collatz chain of " + CollatzLength.ToString()); } else { Console.WriteLine("No argument detected. Using 42 which results in a Collatz chain of length " + CollatzLength.ToString()); } } } } |
So the above code’s fairly simple. The Program class has a pair of private static variables used to track the length of the chain and the current number that we’re evaluating using the Collatz Function. If the user passes in an argument, we’ll calculate the chain from that. Otherwise, we’ll calculate the chain from 42. Next, we assign a block encapsulating the Collatz Function to the CollatzWrapper’s “method” variable. Notice that the block that I’m constructing here references private members of the Program class, and I’m assigning this block to a variable in the CollatzWrapper class. The intuition of most OOP folks will tell them that these private members shouldn’t be accessible from another class.
Thanks to the magic of closures, however, the block that I assigned to the CollatzWrapper’s “method” variable can still reference anything to which it had access when that block was created. This means that it can still access those private members, even from another class.
The results of running the above app with no arguments:
No argument detected. Using 42 which results in a Collatz chain of length 8 Press any key to continue . . .
Results when I pass in argument 17298 (randomly generated using the “ask a friend to pick a large number” method):
17298 results in a Collatz chain of 53
So the closure passed into the wrapper uses private variables from the class in which it was created (rather than the one in which it was executed) to track the length of the Collatz chain and to store the result of its computations.
One other nice thing about closures (not really demonstrated in this code), is that since closures are first-class objects, they can be passed around in exactly the same way as any other object. Moreover, they can be changed, assigned, and moved all through a system, all while maintaining access to the variables that were available to them on creation. These are all properties which make closures full of awesome.
Now, is there anything you can do with closures that you can’t do some other way in C#? Well, no. But I imagine there are a lot of things that closures make easier, more intuitive, and quicker to implement. Besides, even if one never uses closures, it’s nice to be able to say that we know what our tools can do.

No comments yet.