2013-03-21 68 views
1

這個問題是關於結構的。在IEnumerable foreach vs List中修改結構<T> .ForEach

說我定義:

struct Complex 
{ 
    public double real, imaginary; 
} 

如果我嘗試:

var numbers = new[] 
{ 
    new Complex() { real = 1, imaginary = 1 }, 
    new Complex() { real = -1, imaginary = -1 } 
}; 

foreach (var z in numbers) 
{ 
    z.real += 1; 
} 

我得到的編譯錯誤:Error: cannot modify members of 'complex' because it is a 'foreach iteration variable'

然而,

var numbers = new List<Complex>(); 
numbers.Add(new Complex() { real = 1, imaginary = 1 }); 
numbers.Add(new Complex() { real = -1, imaginary = -1 }); 

numbers.ForEach(z => z.real += 1); 

編譯沒有埃羅河牢記'foreach'編譯錯誤,是否有任何理由不在這裏給出編譯時錯誤?

+1

他們是同樣危險的事情要嘗試,但一個容易讓編譯器發現,另一個是非常困難的。 – MattW 2013-03-21 17:41:45

回答

7

TL; DR:

  • C#嘗試來保護你自己從
  • 如果嘗試夠硬,你就可以做壞事 - 語言不保護你你可能做的每一愚蠢的事情
  • 可變的結構是壞

Is there any reason for not giving a compile-time error here then?

是的。你只是修改一個參數,這是絕對好的。參數不被視爲只讀,而foreach循環中的迭代變量爲被視爲只讀。從C#5規範的第8.8.4:

During execution of a foreach statement, the iteration variable represents the collection element for which an iteration is currently being performed. A compile-time error occurs if the embedded statement attempts to modify the iteration variable (via assignment or the ++ and -- operators) or pass the iteration variable as a ref or out parameter.

的這一切都意味着,使用可變變量是一個好主意 - 而且這也並不意味着你ForEach循環會做你想讓它是什麼。在foreach版本(該參數是由值畢竟傳遞給委託

如果你真的想要做同樣的事情(同樣無效,但嘿...),只寫了方法在該變量的結構領域,然後從循環調用它。這是完全有效的......

// Bad code! Valid, but horrible 
foreach (var z in numbers) 
{ 
    z.SetReal(z.real + 1); 
} 

它仍然不會修改列表中的值...但它不會在編譯時失敗。

+2

很高興我沒有打擾回答,因爲我知道Skeet爵士並不遙遠。 – 2013-03-21 17:38:24