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

时间:2012-09-13 12:00:07

标签: c# events event-handling 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)
{
    // ...
}

2 个答案:

答案 0 :(得分:3)

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

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

原因如下:如果没有该临时变量,所有匿名方法都会引用相同的实例,这会在您遍历所有规则时发生变化。这称为"access to modified closure"。正如erikkallen所提到的,这已在C#5中得到修复。

您可以轻松地自行检查:在ControlPreRender和第一个断点点击make an object ID为规则参数设置断点。您将看到在断点的所有以下命中时,rule参数将具有相同的对象ID,这意味着它是完全相同的实例。

答案 1 :(得分:1)

Eric Lippert发布​​了two excellent blog posts的相关信息。您会注意到它们实际上对.NET 4.5进行了重大更改,以使foreach循环的行为与您预期的一样。 (Eric指的是C#5,令人困惑的是用于.NET 4.5的编译器版本。)Eric说,

  

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

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