C#对象引用和操作类型

时间:2016-02-16 17:24:21

标签: c# lambda reference

我希望在C#中有一个关于Action类型和Lambda的快速问题。这是代码:

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

        for (int I = 0; I < 10; I++)
            actions.Add(new Action(() => Print(I.ToString())));

        foreach (Action a in actions)
        {
            a.Invoke();
        }
        actions.Clear();

        int X;
        for (X = 0; X < 10; X++)
        {
            int V = X;
            actions.Add(new Action(() => Print(V.ToString())));
        }

        foreach (Action a in actions)
        {
            a.Invoke();
        }
        Console.ReadLine();
    }


    public static void Print(string s)
    {
        Console.WriteLine(s);
    }

如果您运行此代码,您将看到它连续十次输出10次,然后第二次输出数字0-9。它显然与我使用X vs I的方式有关,以及我如何在第二个循环中每次给我的动作一个新变量V ...可能每个新V都是内存中的新地址,但我是努力理解为什么I.ToString()在第一个循环中没有做同样的事情...为什么第一个Action中使用的I.ToString()与第二个例子的工作方式不同?

3 个答案:

答案 0 :(得分:5)

编译器有效地扩展了for循环:

{
  int I;
  for (I = 0; I < 10; I++)
  {
    actions.Add(new Action(() => Print(I.ToString())));
  }
}

这意味着所有lambda实例都捕获I的同一个实例,当循环退出时它将为10。

在第二个示例中,将值复制到范围为for语句主体的变量中,并且lambda捕获此本地。每次重复循环都会有一个独特的局部。

重要的是要意识到你没有捕获变量的值,而是捕获变量本身。这就是为什么第一个例子不起作用,但第二个例子不起作用。

答案 1 :(得分:2)

这是因为它只是一个委托,当你实际调用它时它会被执行,并且在调用它时,所有动作都具有为i设置的最后一个值,而在foreach循环的情况下它会生成值的本地副本,因此每个操作都有自己的值,使其打印为0-9。

在第一个示例中,当您在foreach循环中第一次调用委托时,i值得到评估,此时i中有 10 ,在第二个示例中您将值存储在本地,该局部模仿foreach循环所执行的相同行为,因为foreach循环也会生成值的本地副本。

您还可以阅读this explanation of Jon Skeet,并在那里链接到2个eric lippert的帖子,这些帖子可以为您提供更多帮助。

答案 2 :(得分:1)

在第一个循环中创建Action时,真正保存的是调用Print()方法,并且在调用之前不会获得变量I的值转到Invoke()方法,但是在循环结束并且变量I的值为10之后会发生这种情况。

在第二种情况下,您将在每次迭代时创建一个新变量V,这样当您执行操作时,每个操作都会调用其中创建的变量V的值。迭代。