How to tell a lambda function to capture a copy in

2019-01-02 18:54发布

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

4条回答
美炸的是我
2楼-- · 2019-01-02 19:20

Remember that lambda expressions are really only syntactic sugar for anonymous methods.

That being said, what you are really looking for is how anonymous methods use local variables in a parent scope.

Here's a link describing this. http://www.codeproject.com/KB/cs/InsideAnonymousMethods.aspx#4

查看更多
与风俱净
3楼-- · 2019-01-02 19:25

The only solution I've been able to find is to make a local copy first:

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

But I'm having trouble understanding why putting a copy inside the for-loop is any different than having the lambda capture i.

查看更多
梦寄多情
4楼-- · 2019-01-02 19:26

The only solution is to make a local copy and reference that within the lambda. All variables in C# (and VB.Net) when accessed in a closure will have reference semantics vs. copy/value semantics. There is no way to change this behavior in either language.

Note: It doesn't actually compile as a reference. The compiler hoists the variable into a closure class and redirects accesses of "i" into a field "i" inside the given closure class. It's often easier to think of it as reference semantics though.

查看更多
长期被迫恋爱
5楼-- · 2019-01-02 19:27

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));
}
查看更多
登录 后发表回答