Cet article est disponible en français.
 
After a quick chat with Eric Lippert about a post  on the use in lambda expressions of a local variable declared in a foreach loop, Eric pointed me out that this piece of code :

[code:c#]

    int a = 0;
    Action action = () => Console.WriteLine(a);
    action();

[/code]

 
Is actually not expanded by the compiler to this code :

[code:c#]

    [CompilerGenerated]
    private sealed class <>c__DisplayClass1
    {
       public int a;

       public void <Main>b__0()
       {
          Console.WriteLine(this.a);
       }
    }

    void Main()
    {
       int a = 0;

       var display = new <>c__DisplayClass1();
       display.a = a;

       var action = new Action(display.<Main>b__0);

       action();
    }

[/code]

 
I made the assumption that a local variable was simply copied in the "DisplayClass" if it is not used after the creation of the lambda, which  is not the case.

If we take this slightly different sample :


[code:c#]

    int a = 0;
    Action action = () => Console.WriteLine(a);
    a = 42;
    action();

[/code]

 
My assumption would have made this code display "0". This is correct because lambda expressions (and anonymous methods) "capture" the variable and not the value; The execution must display 42.

Actually, this latter piece of code is expanded like this:

[code:c#]

    var display = new <>c__DisplayClass1();

    display.a = 0;

    var action = new Action(display.<Main>b__0);

    display.a = 42;

    action();

[/code]

 
We can see that in fact, the variable that was previously local, on the stack, has been "promoted" as a field memberr of the "Display Class". This means that all references to this "local" variable, inside or outside of the lambda, are replaced to point to the current instanc e of the "DisplayClass".

This is quite simple actually, but we can feel that the "magic" behind the C# 3.0 syntactic sugar is the result of a lot of thinking !

I will end this post by a big thanks to Eric Lippert, who took the time to answer me, even though he's probably under heavy load with the developement of C# 4.0. (With the contravariance of generics, yey !)