修改IEnumerable foreach与List. foreach中的结构体

本文关键字:foreach 结构体 IEnumerable List 修改 | 更新日期: 2023-09-27 18:05:14

这个问题是关于结构体的。

说我定义:

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"编译错误,这里有没有理由不给出编译时错误?

修改IEnumerable foreach与List<T>. foreach中的结构体

TL;

    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);
}

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