40

我一直在学习 C#,我正在尝试理解 lambdas。在下面的这个示例中,它打印了 10 十次。

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();
    }
}

显然,在 lambda 后面生成的类存储了一个引用或指向int i变量的指针,并且每次循环迭代时都会为同一个引用分配一个新值。有没有办法强制 lamda 获取副本,例如 C++0x 语法

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

对比

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

4 回答 4

32

编译器正在做的是将您的 lambda 和 lambda 捕获的任何变量拉入编译器生成的嵌套类。

编译后,您的示例看起来很像这样:

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);
                }
        }
}

通过在 for 循环中创建副本,编译器在每次迭代中生成新对象,如下所示:

for (int i = 0; i < 10; ++i)
{
    DisplayClass1 displayClass1 = new DisplayClass1();
    displayClass1.i = i;
    actions.Add(new Action(displayClass1.Lambda));
}
于 2009-01-16T20:24:22.667 回答
29

我能找到的唯一解决方案是先制作本地副本:

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

但我无法理解为什么将副本放入 for 循环与使用 lambda capture 有何不同i

于 2009-01-16T20:08:57.050 回答
11

唯一的解决方案是制作本地副本并在 lambda 中引用它。在闭包中访问时,C#(和 VB.Net)中的所有变量都将具有引用语义与复制/值语义。任何一种语言都无法改变这种行为。

注意:它实际上并不作为参考编译。编译器将变量提升到闭包类中,并将对“i”的访问重定向到给定闭包类中的字段“i”。不过,通常更容易将其视为引用语义。

于 2009-01-16T20:13:05.670 回答
4

请记住,lambda 表达式实际上只是匿名方法的语法糖。

话虽如此,您真正要寻找的是匿名方法如何在父范围内使用局部变量。

这是一个描述这个的链接。 http://www.codeproject.com/KB/cs/InsideAnonymousMethods.aspx#4

于 2009-01-16T20:20:51.847 回答