簡體   English   中英

關於foreach和代表的問題

[英]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);
    });

或者您可以使用任何其他合適的Linq方法 - 例如選擇

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM