2010-08-12 62 views
3

我有一段,其過濾使用LINQ列表代碼,創建一個匿名類型的實例的列表,以及事件處理程序分配到每一個實例:問題的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事件傳遞1SetTrackedLine,不管「SIN( x)「被點擊或」cos(x)「是。

我的問題是,爲什麼會發生這種情況? item.Slot不應該引用匿名類型的每個單獨實例嗎?

謝謝。

回答

9

你是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); 
} 
+0

啊,那清除它,和那篇文章也是一個有趣的閱讀。非常感謝你! – mgbowen 2010-08-12 06:19:52