我有一段代码使用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
,两个Click
个事件将1
传递给SetTrackedLine
,无论是点击“sin(x)”还是“cos(x)”。
我的问题是,为什么会这样? item.Slot
不应该引用匿名类型的每个单独实例吗?
感谢。
答案 0 :(得分:8)
你是closing over the loop variable。具体问题在于:
(s, e) => graph.SetTrackedLine(item.Slot)
^^^^
使用的item
的值将是lambda表达式运行时的当前值,而不是创建时的值。这是C#的“陷阱”和常见错误。
请改为尝试:
foreach (var item in items)
{
var item2 = item;
item2.Menu.Click += (s, e) => graph.SetTrackedLine(item2.Slot);
menuTrackLineWithMouse.DropDownItems.Add(item2.Menu);
}