2013-10-03 120 views
3

我不能很好地理解兩個代碼塊之間的區別。考慮有一個程序C#關閉沒有按預期工作

class Program 
{ 
    static void Main(string[] args) 
    { 

     List<Number> numbers = new List<Number> 
            { 
             new Number(1), 
             new Number(2), 
             new Number(3) 
            }; 


     List<Action> actions = new List<Action>(); 
     foreach (Number numb in numbers) 
     { 
      actions.Add(() => WriteNumber(numb)); 
     } 

     Number number = null; 
     IEnumerator<Number> enumerator = numbers.GetEnumerator(); 
     while (enumerator.MoveNext()) 
     { 
      number = enumerator.Current; 
      actions.Add(() => WriteNumber(number)); 
     } 

     foreach (Action action in actions) 
     { 
      action(); 
     } 

     Console.ReadKey(); 


    } 

    public static void WriteNumber(Number num) 
    { 
     Console.WriteLine(num.Value); 
    } 

    public class Number 
    { 
     public int Value; 

     public Number(int i) 
     { 
      this.Value = i; 
     } 

    } 

} 

輸出是

1 
2 
3 
3 
3 
3  

的代碼,這兩個塊應相同方式工作。但是你可以看到閉合不適用於第一個循環。我錯過了什麼?

在此先感謝。

+0

這是破碎的'foreach'關閉問題嗎? (http://stackoverflow.com/questions/512166/c-sharp-the-foreach-identifier-and-closures) – sircodesalot

+1

@sircodesalot:相反的排序 - 這是'foreach'由於C#5修復了這個問題。 –

+0

哦,他們修好了。很高興知道,謝謝! – sircodesalot

回答

3

您聲明number變量超出while循環。對於每個數字,您將其參考存儲在您的number變量中 - 每次覆蓋最後一個值。

您應該在while循環中移動聲明,以便爲每個數字創建一個新變量。

IEnumerator<Number> enumerator = numbers.GetEnumerator(); 
    while (enumerator.MoveNext()) 
    { 
     Number number = enumerator.Current; 
     actions.Add(() => WriteNumber(number)); 
    } 
+0

這是真的:) – Dmitry

3

的代碼,這兩個塊應相同方式工作。

不,他們不應該 - 至少在C#5,在C#3和4,他們,其實。

但是在foreach循環中,在C#5中,每循環迭代一個變量。您的lambda表達式捕獲該變量。循環的後續迭代創建不同的變量,這些變量不會影響先前捕獲的變量。

while環,你有一個變量,所有迭代捕獲。對該變量的更改將在全部中看到捕獲它的代表。你可以通過你的while循環後,加入這一行看到這一點:

number = new Number(999); 

那麼你的產出將是

1 
2 
3 
999 
999 
999 

現在,在C#3和4,foreach規範是基本的設計打破 - 它會捕獲所有迭代中的單個變量。然後在C#5中修復這個問題,以便在每次迭代中使用一個單獨的變量,這基本上就是您使用該類代碼時所需要的總是

1

在你的循環:

Number number = null; 
    IEnumerator<Number> enumerator = numbers.GetEnumerator(); 
    while (enumerator.MoveNext()) 
    { 
     number = enumerator.Current; 
     actions.Add(() => WriteNumber(number)); 
    } 

數量的循環範圍之外聲明。所以當它被設置爲下一個當前的迭代器時,所有引用數字的動作也會更新到最新的。所以當你運行每個動作時,他們都會使用最後一個數字。

0

感謝您的答案。但我想我被誤解了。我想閉嘴工作。這就是爲什麼我設置循環變量超出範圍。問題是:爲什麼它在第一種情況下不起作用?我忘了提及我使用C#3.5(不是C#5.0)。所以soop變量應該被定義爲超出範圍,兩個代碼塊的工作方式相同。

+0

你真的應該添加到您的問題。這不是一個真正的答案。 – juharr