修改IEnumerable foreach与List <t> .ForEach </t>中的结构

时间:2013-03-21 17:33:40

标签: c# ienumerable

这个问题是关于结构的。

我说定义:

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 个答案:

答案 0 :(得分:7)

TL; DR:

  • C#尝试以保护您自己
  • 如果你努力学习,你就能做坏事 - 这种语言不会保护你免受你可能做的每一件愚蠢的事情
  • 可变结构不好
  

有没有理由不在这里给出编译时错误?

是。你只是修改一个参数,这绝对没问题。参数不被认为是只读的,而foreach循环中的迭代变量被认为是只读的。从C#5规范的第8.8.4节开始:

  

在执行foreach语句期间,迭代变量表示当前正在执行迭代的集合元素。如果嵌入语句尝试修改迭代变量(通过赋值或++和 - 运算符)或将迭代变量作为ref或out参数传递,则会发生编译时错误。

这一切都不意味着使用可变变量是一个好主意 - 它也并不意味着你的ForEach循环会按照你的意愿行事。 (毕竟,参数是按值传递给代理。)

如果你真的想在foreach版本中做类似的事情(同样无效,但是嘿......),只需在结构中编写一个方法来改变字段,然后从循环中调用它。这完全有效......

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

它仍然不会修改列表中的值...但它不会在编译时失败。