Understanding Variable Capturing in C#

With the addition of anonymous delegates in C# 2.0 and with lambda expressions in C# 3.0 you might have been hearing a lot about variable capturing.  This is the mechanism in which the delegate/lambda which was defined inline is able to hold on to any variables within its lexical scope. 

For example in this code:

    1: static void Main(string[] args)
    2: {
    3:     Console.WriteLine("Variable Capturing");
    4:  
    5:     string name = "Matthew";
    6:     Func<String> capture = () => name;
    7:  
    8:     name = "Mallory";
    9:  
   10:     Print(capture);
   11:  
   12: }
   13:  
   14: static void Print(Func<string> capture)
   15: {
   16:     Console.WriteLine(capture());
   17: }

 

The variable "name" is captured inside of the lambda expression on line 6.  The same variable is then modified on line 8.  Then on line 10 we pass the lambda expression to the Print function which then prints out the captured variable. WHen the code is run the output will be:

Variable Capturing

Mallory

 

The thing worth noting is that although the variable  "name" is "captured" on line 6, it reflects the change made to the local variable "name" on line 8.  To understand how this works lets show what the C# compiler will approximately turn the previous code into.  (I changed names from anonymous types to look more readable):

    1: public class Capture
    2: {
    3:     public string name;
    4:  
    5:     public string Lambda()
    6:     {
    7:         return this.name;
    8:     }
    9:  
   10: }
   11:  
   12: static void Main(string[] args)
   13: {
   14:     Capture capture = new Capture();
   15:  
   16:     Console.WriteLine("Variable Capturing");
   17:  
   18:     capture.name = "Matthew";
   19:  
   20:     capture.name = "Mallory";
   21:  
   22:     Print(capture.Lambda);
   23: }
   24:  
   25: static void Print(Func<string> capture)
   26: {
   27:     Console.WriteLine(capture());
   28: }

 

A new class was created called "Capture".  All occurrence of the variable "name" in the original code have been replaced by a field access to the "name" member of the capture class.  Also, the lambda expression becomes just a method on the capture class which I called Lambda (yep, that is all anonymous delegates and lambda expression really are).  This is how changes in the variable get persisted in the lambda expression, every change to the variable just modifies the field in the Capture class.

 

That is all there is to it, simple!