循环操作列表

时间:2012-03-05 15:33:47

标签: 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();

输出:

ghi
ghi
ghi

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

abc
def
ghi

3 个答案:

答案 0 :(得分:14)

您的操作是一个闭包,因此它会访问str本身,而不是str的副本:

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

答案 1 :(得分:4)

此行为由Closures调整。

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

如果您编写代码(如提供的答案),则强制C#编译器每次都重新生成值,因此地址将被传递给labmda,所以每个lambda都会有自己的变量。

顺便说一句,如果我没有错,C#团队承诺在C# 5.0中修复此非自然行为。所以最好在这个主题上查看他们的博客,以便将来更新。

答案 2 :(得分:3)

这是一个相当复杂的情况。简短的回答是在将局部变量赋值给闭包之前创建它的副本:

string copy = str;
actions.Add(new Action(() => { Trace.WriteLine(copy); }));

Check out this article on closures了解更多信息。