[英]Question about foreach and delegates
假設以下代碼:
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。
這種方法怎么樣? 還有其他更好的解決方案嗎?
更新:這里有關於這個問題的廣泛分析和評論:
http://ericlippert.com/2009/11/12/closing-over-the-loop-variable-considered-harmful-part-one/
這是一個非常頻繁報道的問題; 通常它被報告為編譯器錯誤,但實際上編譯器根據規范做了正確的事情。 匿名函數關閉變量 ,而不是值 ,並且只有一個foreach循環變量。 因此,每個lambda關閉同一個變量,因此獲取該變量的當前值。
這對幾乎每個人來說都是令人驚訝的,並且導致很多混亂和許多錯誤報告。 我們正在考慮更改C#的假設未來版本的規范和實現,以便循環變量在循環結構中邏輯聲明,每次循環都給出一個“新”變量。
這將是一個突破性的變化 ,但我懷疑依賴這種奇怪行為的人數很少。 如果您對此主題有任何意見,請隨時在上述更新中提及的博客文章中添加評論。 謝謝!
第二塊代碼就是最好的方法,你可以讓所有其他東西保持不變。
但是,可以在Something
上創建一個屬性為Item
的屬性。 反過來,事件代碼可以從事件的發送者訪問此Item
,或者它可能包含在事件的eventargs中。 因此消除了關閉的需要。
就個人而言,我添加了“消除不必要的關閉”作為一個有價值的重構,因為很難對它們進行推理。
如果ItemCollection是(通用)List ,則可以使用其ForEach -method。 每個我會給你一個新的范圍:
ItemCollection.ForEach(
i =>
{
Something s = new Something();
s.EventX += delegate { ProcessItem(i); };
SomethingCollection.Add(s);
});
var somethings = ItemCollection.Select(
i =>
{
Something s = new Something();
s.EventX += delegate { ProcessItem(i); };
return s;
});
foreach(Something s in somethings)
SomethingCollection.Add(s);
您在這里面臨的問題與諸如閉包之類的語言構造有關。 第二段代碼修復了這個問題。
foreach(Item i on ItemCollection)
{
Something s = new Something(i);
s.EventX += (sender, eventArgs) => { ProcessItem(eventArgs.Item);};
SomethingCollection.Add(s);
}
你不會只是將'i'傳遞到你的'Something'類並在EventX的事件args中使用它
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.