2009-10-21 55 views
5

假設下面的代碼:關於產品的foreach和代表

foreach(Item i on ItemCollection) 
{ 
    Something s = new Something(); 
    s.EventX += delegate { ProcessItem(i); }; 
    SomethingCollection.Add(s); 
} 

當然,這是錯誤的,因爲所有代表指向同一個項目。替代方案是:

foreach(Item i on ItemCollection) 
{ 
    Item tmpItem = i; 
    Something s = new Something(); 
    s.EventX += delegate { ProcessItem(tmpItem); }; 
    SomethingCollection.Add(s); 
} 

在這種情況下,所有代表都指向自己的Item。

這種方法呢?還有其他更好的解決方案嗎?

+0

你可以發佈編譯和顯示差異的完整代碼嗎? – empi 2009-10-21 12:55:31

+0

你可以使用C#1.0反編譯第一段代碼,你會看到有什麼區別 – 2009-10-21 12:58:09

回答

11

UPDATE:有廣泛的分析和評論就這個問題在這裏:

http://ericlippert.com/2009/11/12/closing-over-the-loop-variable-considered-harmful-part-one/


這是一個非常頻繁報告的問題;通常它被報告爲編譯器錯誤,但實際上編譯器根據規範正在做正確的事情。匿名函數關閉變量,而不是,並且只有一個foreach循環變量。因此,每個lambda關閉相同的變量,因此獲取該變量的當前值。

這是令人驚訝的幾乎所有人,並導致許多混亂,許多錯誤報告。我們是,考慮到更改了C#的假設未來版本的規範和實現,因此循環變量在循環構造內部被邏輯聲明,每次循環時都會給出一個「新鮮」變量。

這將是一個突破變化,但我懷疑依賴這種奇怪行爲的人數很低。如果您對此主題有意見,請隨時向上述更新中提到的博客帖子添加評論。謝謝!

+1

而且這不是更好顯示編譯器警告而不是改變行爲? – FerranB 2009-10-26 11:53:01

+1

事實上,在這種情況下,編譯器警告*可能會被保證。目前還不清楚哪個更好*。我們會考慮兩者。 – 2009-11-03 19:48:06

0

你面對這裏的問題是關係到這種語言結構作爲關閉。 第二段代碼修復了這個問題。

5

代碼的第二塊差不多是最好的方法,你可以得到所有其他的事情保持相同。

但是,有可能在Something上創建一個屬性,其中需要Item。反過來,事件代碼可以從事件的發件人訪問此Item,或者它可能包含在事件的事件欄中。因此消除了關閉的需要。

個人而言,我已經添加了「不必要的封閉消除」作爲一個有價值的重構,因爲它可能是困難的原因在他們身上。

+0

是啊我在想什麼,但更多雄辯:) – Hath 2009-10-21 13:03:08

0
foreach(Item i on ItemCollection) 
{ 
    Something s = new Something(i); 
    s.EventX += (sender, eventArgs) => { ProcessItem(eventArgs.Item);}; 
    SomethingCollection.Add(s); 
} 

,你會不會只是傳遞的「I」到你的「東西」類,並用它在EventX的事件參數

1

如果ItemCollection是(generic) List你可以使用它ForEach - 方法。它會給你一個新的範圍,每個I:

ItemCollection.ForEach(
    i => 
    { 
     Something s = new Something(); 
     s.EventX += delegate { ProcessItem(i); }; 
     SomethingCollection.Add(s); 
    }); 

或者你可以使用任何其他合適的Linq - 方法 - 像Select

var somethings = ItemCollection.Select(
     i => 
     { 
      Something s = new Something(); 
      s.EventX += delegate { ProcessItem(i); }; 
      return s; 
     }); 
foreach(Something s in somethings) 
    SomethingCollection.Add(s); 
+0

我不會推薦這些作爲解決方案,雖然... – 2009-10-21 13:44:47