循环操作列表

dem*_*key 8 c# action enumeration

我无法理解如何遍历Action列表.当我尝试它时,我最终得到的值与前一次迭代相同.

这是代码(简化示例):

string[] strings = { "abc", "def", "ghi" };

var actions = new List<Action>();
foreach (string str in strings)
    actions.Add(new Action(() => { Trace.WriteLine(str); }));

foreach (var action in actions)
    action();
Run Code Online (Sandbox Code Playgroud)

输出:

ghi
ghi
ghi
Run Code Online (Sandbox Code Playgroud)

为什么在strings执行操作时始终选择最终元素?
我怎样才能实现所需的输出:

abc
def
ghi
Run Code Online (Sandbox Code Playgroud)

Mat*_*eid 15

你的动作是一个闭包,因此它访问str自己,而不是一个副本str:

foreach (string str in strings)
{
    var copy = str; // this will do the job
    actions.Add(new Action(() => { Trace.WriteLine(copy); }));
}
Run Code Online (Sandbox Code Playgroud)


Tig*_*ran 6

这种行为受Closures 制约

存在于 lambda 中的变量是引用不是值副本。这意味着指向 假定的最新值str,在您的情况下为“ghi”。这就是为什么每次调用它都会转到相同的内存位置并自然地恢复相同的值。

如果您编写代码,就像在提供的答案中一样,您会强制C#编译器每次重新生成一个值,因此将向 labmda 传递一个地址,因此每个 lambda 都会有它自己的变量。

顺便说一句,如果我没有弄错,C#团队承诺来解决这个非天然的行为C# 5.0。所以最好查看他们关于这个主题的博客以获取未来的更新。

  • +1 很好的解释。值得一提的是,Java 的做法是另一种方式。如果你处理 `Runnable`(通常)来启动一个线程,变量会被*复制*到新的上下文中。这也是为什么 Java 强迫您将它们设为“final”。 (3认同)