2011-04-06 53 views
4


最近我讀了C#的深度和它,我一直在使用他們考績數據點擊事件,與此類似的教我什麼是lambda表達式:如何避免在lambda表達式中捕獲變量?

image.MouseDown+=(o ,e)=>MethodToDoSomething(DataNeededForAction); 

現在的問題是變量當在foreach循環中使用時捕獲(感謝Jon Skeet使這部分真的很清楚:),當初始化有我訂閱的事件的幾個對象時,我通常會遇到變量捕獲問題。請看下面的例子:

foreach (var game in GamesCollection) 
{ 
    Image img = new Image(); 
    img.Width = 100; 
    img.MouseDown+=(o,e) => MyMethod(game.id); 
} 

爲了避免在這種情況下,我必須添加一些變量指定的遊戲,然後將該變量傳遞給方法捕獲,這將創建額外的代碼不清且多,更加雜亂。 有沒有辦法繞過這個?有些東西至少會看起來更清潔?

Thx,Ziv

回答

5

(編輯:請注意,這與C#5,其中foreach現在實際上會爲每個迭代一個新的變量發生變化。)

沒有,有沒有避免這種方式。基本上,語言規範在單個變量中描述的foreach的方式是錯誤的,而不是每次迭代的新變量。就個人而言,我不認爲它是涉及代碼量的問題 - 當你意識到這是一個問題並想出如何解決問題時,你已經完成了最大的障礙。我通常會給維護程序員一個明顯的評論,讓你介意。我敢肯定,如果C#團隊從頭開始,他們會做不同的事,但他們不是。哎,they've even discussed changing the existing behaviour ...但有很好的理由改變(特別是在C#N + 1編譯器上正常工作的代碼仍然可以編譯,但在C#N編譯器上給出錯誤的結果;一個非常微妙的來源針對希望用多個編譯器構建代碼的開源庫作者的缺陷)。

(希望您會喜歡這本書,順便說一句...)

+0

由於寫了這個答案,因此C#團隊決定在C#5中循環內移動循環變量(鏈接文章被更新)。現在問題中的代碼不需要複製'遊戲'變量。 – 2012-11-06 19:39:49

+0

@AndersAbel:的確如此。不幸的是,有太多的答案回到現在修復它們:( – 2012-11-06 19:41:25

2

總之,沒有。您需要創建額外的存儲空間,以便在方法關閉中保留game的正確實例。

+0

我明白這是編譯器的限制,但我不知道是否有可能改善編譯器檢測到它,並自動執行它。有沒有技術上的理由,這是不可能的? – 2011-04-06 23:11:09

+0

這根本不是它的工作原理。 foreach只使用單個變量聲明來存儲Enumerator.Current,每次傳遞都會被覆蓋。這是在方法關閉中捕獲的內容。我沒有看到它需要改變。 – spender 2011-04-06 23:14:34

+0

我知道這不是它的工作方式,但我在問爲什麼做這樣做的決定。編譯器可能會檢測到迭代變量被捕獲並通過自動添加臨時分配來對其進行解釋。使用閉包的人幾乎遇到的每個問題都是由於捕獲迭代變量。我問是否有任何技術/邏輯的原因,爲什麼未來的語言/編譯器無法自動化,因爲我想不出任何人想要捕獲迭代變量的任何實際原因。 – 2011-04-06 23:19:39

0

如果GamesCollectionList<T>,你可以使用ForEach方法:

GamesCollection.ForEach(game => { 
    Image img = new Image(); 
    img.Width = 100; 
    img.MouseDown+=(o,e) => MyMethod(game.id); 
}); 

但總的來說,沒有。還有一個額外的變量並不是那麼混亂。

+0

當然,但實際上這會爲您提供給ForEach的代理的參數中的GamesCollection中的每個遊戲創建一個新的引用,所以它的確非常相同。 – spender 2011-04-06 22:48:32

+0

@spender權利。當我讀到這個問題時,他試圖避免額外的代碼行,不一定是額外的引用。 – 2011-04-06 22:54:53

-1

首先用LINQ表達式拉出game.id怎麼樣?通過.Aggregate()應用程序調用應該每次都創建一個新變量,因爲它實際上是一個方法參數。

GamesCollection.Select(game => game.id).Aggregate((_, id) => { 
    Image img = new Image(); 
    img.Width = 100; 
    img.MouseDown+=(o,e) => MyMethod(id); 
    return img; 
});