LINQ、匿名类型和闭包的问题

3

我有一段使用LINQ过滤列表的代码,创建了一个匿名类型实例的列表,并为每个实例分配了一个事件处理程序:

// Select every linear expression and create a menu item from it
var items = from expr in expressionList.Expressions
            where expr.Type == ExpressionType.Linear
            let stdExpr = (StandardExpression)expr
            select new
            {
                Menu = new ToolStripMenuItem(stdExpr.Expression), // string
                stdExpr.Slot // int
            };

// Wire a Click event handler to each menu to set the tracked line
foreach (var item in items)
{
    item.Menu.Click += (s, e) => graph.SetTrackedLine(item.Slot);

    menuTrackLineWithMouse.DropDownItems.Add(item.Menu);
}

这种方法在事件处理程序的连接和菜单的正确添加方面表现良好。问题出在当菜单项被点击并且处理程序被触发时。无论哪个菜单项触发了处理程序,只有最后一个被传递到SetTrackedLine
例如,如果我有两个菜单,"sin(x)",槽位为0,和"cos(x)",槽位为1,则无论是点击"sin(x)"还是"cos(x)",所有Click事件都会将1传递给SetTrackedLine
我的问题是,为什么会这样?item.Slot不应该指向匿名类型的每个独立实例吗?
谢谢。
1个回答

9

您正在进行闭合循环变量。问题具体在这里:

(s, e) => graph.SetTrackedLine(item.Slot)
                               ^^^^
< p >当 lambda 表达式被执行时,所使用的 < code >item 值将为当前值,而不是创建时的值。 这是 C# 的一个 "陷阱" 和常见错误。 < p >相反,请尝试这样做:
foreach (var item in items)
{
    var item2 = item;
    item2.Menu.Click += (s, e) => graph.SetTrackedLine(item2.Slot);
    menuTrackLineWithMouse.DropDownItems.Add(item2.Menu);
}

啊,这下明白了,那篇文章也很有趣。非常感谢! - user153498

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接