Search code examples
c#loopslambdacapture

How to tell a lambda function to capture a copy instead of a reference in C#?


I've been learning C#, and I'm trying to understand lambdas. In this sample below, it prints out 10 ten times.

class Program
{
    delegate void Action();
    static void Main(string[] args)
    {
        List<Action> actions = new List<Action>();

        for (int i = 0; i < 10; ++i )
            actions.Add(()=>Console.WriteLine(i));

        foreach (Action a in actions)
            a();
    }
}

Obviously, the generated class behind the lambda is storing a reference or pointer to the int i variable, and is assigning a new value to the same reference every time the loop iterates. Is there a way to force the lamda to grab a copy instead, like the C++0x syntax

[&](){ ... } // Capture by reference

vs.

[=](){ ... } // Capture copies

Solution

  • What the compiler is doing is pulling your lambda and any variables captured by the lambda into a compiler generated nested class.

    After compilation your example looks a lot like this:

    class Program
    {
            delegate void Action();
            static void Main(string[] args)
            {
                    List<Action> actions = new List<Action>();
    
                    DisplayClass1 displayClass1 = new DisplayClass1();
                    for (displayClass1.i = 0; displayClass1.i < 10; ++displayClass1.i )
                            actions.Add(new Action(displayClass1.Lambda));
    
                    foreach (Action a in actions)
                            a();
            }
    
            class DisplayClass1
            {
                    int i;
                    void Lambda()
                    {
                            Console.WriteLine(i);
                    }
            }
    }
    

    By making a copy within the for loop, the compiler generates new objects in each iteration, like so:

    for (int i = 0; i < 10; ++i)
    {
        DisplayClass1 displayClass1 = new DisplayClass1();
        displayClass1.i = i;
        actions.Add(new Action(displayClass1.Lambda));
    }