lambda表达式的事件处理程序似乎有错误的参数值



根据这个答案https://stackoverflow.com/a/6457528/299110

我将ctrl.PreRender += (sender, e) => ControlPreRender(ctrl, rule);用于foreach中的一个或多个控件,每次ctrl和rule的值都会更改。

然而,当调用ControlPreRender方法时,rule参数似乎与事件处理程序所连接的发送方不一致

我知道我在这里错过了什么,但不确定是什么!

更新:感谢你的回答,Eric Lippert的博客真的解释了这一点。正如落选者所建议的,我在下面放了更多的代码,希望能稍微改进一下这个问题:

foreach (var ctrl in controls) 
{
    // ...
    foreach (var rule in rules)
    {
        // ...
        ctrl.PreRender += (sender, e) => ControlPreRender(ctrl, rule);
    }
}
public static void ControlPreRender(Control ctrl, ControlRule rule)
{
    // ...
}

我想你想要一个临时变量:

foreach(var rule in rules)
{
    var tmpRule = rule;
    ctrl.PreRender += (sender, e) => ControlPreRender(sender as Control, tmpRule);
}

原因如下:如果没有这个临时变量,所有匿名方法都会引用同一个实例,当您循环所有规则时,该实例会发生变化。这被称为"修改闭包的访问"。正如erikkallen提到的,这已经在C#5中得到了修复。

您可以很容易地自己检查:在ControlPreRender中设置一个断点,并在第一个断点命中时为规则参数创建一个对象ID。您将看到,在断点的以下所有命中处,规则参数都将具有相同的对象ID,这意味着它是完全相同的实例。

Eric Lippert为此发布了两篇出色的博客文章。您会注意到,他们实际上对.NET4.5进行了突破性的更改,使foreach循环的行为与您预期的一样。(Eric指的是C#5,令人困惑的是,它是用于.NET 4.5的编译器版本。)Eric说,

"这是我们收到的最常见的错误错误错误报告。也就是说,有人认为他们在编译器中发现了错误,但事实上编译器是正确的。"

请注意,@Daniel已经根据Eric的帖子发布了正确的代码。

相关内容

  • 没有找到相关文章

最新更新