2011-08-26 53 views
1

我不會問這個問題什麼是關閉。這是一個封閉: 如:C#,Closures和Lambdas

List<Func<int>> add = new List<Func<int>>(); 

List<int> coll = new List<int>(){1,2,3,4,5}; 
foreach (int i in coll) 
{ 
    add.Add(() => i*2); 
} 

由於關閉關閉了變數,毫無疑問其結果將適用於所有情況是10,如果我們試圖調用的「添加」列表中的所有函數功能。這讓我想,如果這是封閉的,那麼下面的例子也應該是封閉的。

//Indirect way of writing the same example 
Enumerable.Range(1, 5).ToList().ForEach(x => add.Add(() => x * 2)); 

這裏也是我們在關閉的變量,因此該變量的狀態應該是變量的最後一個值,但事實證明,事實並非如此。這不是關閉。 lambda是否以不可改變的方式構造其變量,即一旦我們更改x的值,就會創建一個新變量來存儲該值?

+1

請記住,變量是*存儲位置*。你不會指望在lambda的兩個不同的調用中將相同的存儲位置用於形式參數x,因爲對應於x的兩個參數可能在同一時間是不同的*。調用方法爲形式參數和當地人創建新的存儲位置;這些存儲位置都包含在封口中。現在,第一個示例中的「我」只有一個存儲位置。 (我們可能會在下一個版本中改變這種情況,因爲這是一個常見錯誤。) –

+0

我想知道這仍然會被問到多少次...... – leppie

回答

0

在你的第二個例子中,x每次都會改變add.Add--在你的第一個例子中,同樣的變量「i」被關閉。

除此之外,你可以在.net中想到一個閉包,就像一個類對象,捕獲所有上下文中沒有直接給出的「外部」數據。在你的第一個例子中,你可以考慮用一個Method(即i * 2)和一個對你的對象「i」的引用被記住的字段創建一個類。

1

區別在於第一個示例是爲每個代理共享相同的i實例,因爲i在整個循環中被捕獲一次。第二個例子中,每個函數都有1..5的唯一值。

要使第一個示例工作相同,可以在循環中使用局部變量,如下所示,現在分別爲每個函數捕獲x。

foreach (int i in coll) 
    { 
    int x = i; 
    add.Add(() => x * 2); 
    } 


Addtional信息

下面是關於這一主題的兩個部分後通過埃裏克利珀

Closing over the loop variable considered harmful - Part 1

Closing over the loop variable considered harmful - Part 2

0

在你的第二個例子還有封閉是創造編輯,捕獲變量x。變量x是每個調用的新變量。示範也許是爲了。

class SomeClass 
{ 
    List<int> col = new List<int> {1,2,3,4,5}; 

    void SomeFunction() 
    { 
     for (int x = 0; x < 6; x++) 
      ForEachFunction(x); 
    } 

    void ForEachFunction(int x) 
    { 
     // x here is a copy of the variable from the for loop 
     col.Add(x); 
    } 
} 

然而,在您的第一個示例中,我是前面定義的,並且每次調用都重複使用。