Closures in the real world: Part 3

After I wrote my last post about closures, I was fairly clear about how a closure can extend the lifetime of an apparent stack variable:
Closures

What looks like a stack variable isn’t actually a stack variable, it’s an implicit reference to a member variable on an implicitly-defined class representing the closure.

But then another question occurred to me. Closures can extend the lifetime of an otherwise stack-local value type because the value itself lives in the closure and is referenced in the local stack frame. But what if the same variable is referenced in two different closures? Which closure object gets to own the variable, and which gets a reference?

Consider the following example (the same principle applies to my previous example, but this makes the point clearer):

class Program
{
   private static Func<int> MakeAClosure()
   {
      int x = 0;

      Func<int> incrementByOne =
         delegate()
         {
            x += 1;
            return x;
         };

      Func<int> incrementBySix =
         delegate()
         {
            x += 6;
            return x;
         };

      Random rnd = new Random();
      if ((rnd.Next() % 2) == 0)
      {
         return incrementByOne;
      }
      else
      {
         return incrementBySix;
      }
   }

   static void Main(string[] args)
   {
      Func<int> myFunc = MakeAClosure();

      for (int i = 0; i < 10; i++)
      {
         Console.WriteLine("{0}", myFunc());
      }
   }
 }

In this case, two closures are constructed. The apparent stack variable x is not a stack variable, but a reference into one of the closure objects. But which one? The compiler doesn’t know which closure will be returned until runtime, and in any case changes made in one closure (or on the “stack”) ought to affect the other closure.

This diagram shows how things worked in my head, and illustrates the problem:

Closures2

Fortunately, you can see what’s really happening without too much digging into the disassembly:

disassembly

The ugly naming doesn’t help, but note that within the Program class there is a nested class <>c__DisplayClass2.  As we saw before, this is the way that closures are implemented. However, the crucial thing is that there’s only one class at work here, and it holds both closure functions within it (they are the functions called <MakeAClosure>b__x). The fact that we’re returning one of a possible two different closures is actually an illusion: whatever return condition happens we return the entire class and both closure functions. Presumably we somehow also return some sort of reference indicating which of the functions to call when the closure is dereferenced later.

Leave a Reply

Your email address will not be published. Required fields are marked *