【问题标题】:C# object references and Action types [duplicate]C# 对象引用和操作类型 [重复]
【发布时间】:2016-05-28 02:49:32
【问题描述】:

我有一个关于 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 与 I 的方式有关,以及我如何在第二个循环中每次给我的动作一个新变量 V ......可能每个新 V 都是内存中的一个新地址,但我难以理解为什么 I.ToString() 在第一个循环中没有做同样的事情......为什么第一个 Action 中使用的 I.ToString() 的工作方式与第二个示例不同?

【问题讨论】:

标签: c# lambda reference


【解决方案1】:

for 循环被编译器有效地扩展为这个:

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

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

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

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

【讨论】:

  • 感谢您的解释。我会认为 I.ToString() 正在创建一个新变量,但显然情况并非如此。更像是存储了函数调用 I.ToString() 并且 I 的值可能会发生变化,并且在调用之前不会被评估。
【解决方案2】:

Actions 在您的第一个循环中创建时,真正保存的是对Print() 方法的调用,并且直到对Invoke() 方法的调用才获得变量I 的值已完成,但这发生在循环完成并且变量 I 的值为 10 之后。

在第二种情况下,您将在每次迭代中创建一个新变量 V,以便在执行操作时使用在该迭代中创建的变量 V 的值调用每个操作。

【讨论】:

    【解决方案3】:

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

    在第一个示例中,i 值在您第一次在 foreach 循环中调用委托时被评估,当时 i 包含 10,在第二个示例中,您存储本地中的 value 模仿了 foreach 循环所做的相同行为,因为 foreach 循环也制作了 value 的本地副本。

    您也可以阅读this explanation of Jon Skeet,他将链接到 2 eric lippert 的帖子,这将对您有更多帮助。

    【讨论】:

    • 这是一个比接受的答案更好的解释。
    猜你喜欢
    • 2019-04-10
    • 1970-01-01
    • 2014-01-06
    • 2012-05-24
    • 1970-01-01
    • 2017-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多