代码之家  ›  专栏  ›  技术社区  ›  Salman A

如何告诉lambda函数捕获副本而不是C中的引用?

  •  23
  • Salman A  · 技术社区  · 15 年前

    我一直在学习C,我在努力理解兰伯斯。在下面的这个示例中,它打印了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
    

    VS

    [=](){ ... } // Capture copies
    
    4 回复  |  直到 12 年前
        1
  •  24
  •   Tinister    15 年前

    编译器正在做的是将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));
    }
    
        2
  •  10
  •   Salman A    15 年前

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

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

    但是我很难理解为什么在for循环中放置一个副本与使用lambda捕获有什么不同 i .

        3
  •  9
  •   Martin Liversage    12 年前

    唯一的解决方案是在lambda中进行本地复制和引用。当在闭包中访问时,C(和vb.net)中的所有变量都将具有引用语义,而不是复制/值语义。这两种语言都无法改变这种行为。

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

        4
  •  2
  •   Matt Brunell    15 年前

    记住lambda表达式实际上只是匿名方法的语法甜头。

    也就是说,您真正要寻找的是匿名方法如何在父范围中使用局部变量。

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