Problem with LINQ, Anonymous Types, and Closing

I have a piece of code that filters a list using LINQ, creates a list of instances of an anonymous type, and assigns an event handler for each instance:

// 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); } 

This works well in that event handlers are connected and menus are added correctly. The problem occurs when you click on a menu item, and the handler starts. Regardless of which menu item the handler launched, only the last of them is passed to SetTrackedLine .

For example, if I have two menus: β€œsin (x)”, with slot 0 and β€œcos (x)”, with slot 1 , both Click events go through 1 to SetTrackedLine , regardless of whether β€œsin (x) was pressed ) "or" cos (x) ".

My question is: why is this happening? Should item.Slot reference every single instance of an anonymous type?

Thanks.

+3
closures c # linq anonymous-types
source share
1 answer

You close the loop variable . The problem is specifically here:

 (s, e) => graph.SetTrackedLine(item.Slot) ^^^^ 

The item value used will be the current value when the lambda expression is executed, not the value it had when it was created. This is "gotcha" C # and a common mistake.

Try this instead:

 foreach (var item in items) { var item2 = item; item2.Menu.Click += (s, e) => graph.SetTrackedLine(item2.Slot); menuTrackLineWithMouse.DropDownItems.Add(item2.Menu); } 
+8
source share

All Articles