LINQ,匿名类型和闭包问题

3 c# linq closures anonymous-types

我有一段代码使用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);
}
Run Code Online (Sandbox Code Playgroud)

这很有效,因为事件处理程序有线并且菜单被正确添加.单击菜单项时会出现问题,并触发处理程序.无论哪个菜单项触发了处理程序,只传递最后一个SetTrackedLine.

一个例子是,如果我有两个菜单,"sin(x)",带槽0,"cos(x)",带槽1,两个Click事件都传递1SetTrackedLine,无论是否点击"sin(x)"或"cos( x)"是.

我的问题是,为什么会发生这种情况?不应该item.Slot引用匿名类型的每个单独实例?

谢谢.

Mar*_*ers 8

您正在关闭循环变量.具体问题在于:

(s, e) => graph.SetTrackedLine(item.Slot)
                               ^^^^
Run Code Online (Sandbox Code Playgroud)

itemused 的值将是lambda表达式运行时的当前值,而不是创建它时的值.这是C#的"陷阱"和常见错误.

试试这个:

foreach (var item in items)
{
    var item2 = item;
    item2.Menu.Click += (s, e) => graph.SetTrackedLine(item2.Slot);
    menuTrackLineWithMouse.DropDownItems.Add(item2.Menu);
}
Run Code Online (Sandbox Code Playgroud)