在C#中,为什么不能在foreach循环中修改值类型实例的成员?

| 我知道值类型应该是不可变的,但这只是一个建议,而不是规则,对吗? 那么为什么我不能做这样的事情:
struct MyStruct
{
    public string Name { get; set; }
}

 public class Program
{
    static void Main(string[] args)
    {
        MyStruct[] array = new MyStruct[] { new MyStruct { Name = \"1\" }, new MyStruct { Name = \"2\" } };
        foreach (var item in array)
        {
            item.Name = \"3\";
        }
        //for (int i = 0; i < array.Length; i++)
        //{
        //    array[i].Name = \"3\";
        //}

        Console.ReadLine();
    }
}
代码中的foreach循环无法编译,而带注释的for循环可以正常工作。错误信息:   无法修改\'item \'的成员,因为它是\'foreach迭代变量\' 这是为什么?     
已邀请:
因为foreach使用枚举器,并且枚举器不能更改基础集合,但是可以更改该集合中的对象引用的任何对象。这就是值和引用类型语义起作用的地方。 在引用类型(即类)上,集合所存储的所有内容都是对对象的引用。因此,它实际上从未接触过对象的任何成员,也不会在乎它们。对对象的更改不会影响集合。 另一方面,值类型将其整个结构存储在集合中。在不更改集合并使枚举器无效的情况下,您无法触摸其成员。 而且,枚举器返回集合中值的副本。在ref类型中,这没有任何意义。引用的副本将是相同的引用,并且您可以以所需的任何方式更改引用的对象,且更改会超出范围。另一方面,在值类型上,意味着您得到的只是该对象的副本,因此,对该副本所做的任何更改将永远不会传播。     
从某种意义上说,这是一个建议,没有什么可以阻止您违反它,但是,与“这是一个建议”相比,它确实应该有更多的分量。例如,由于您在这里看到的原因。 值类型将实际值存储在变量中,而不是引用中。这意味着您在数组中具有该值,并且在
item
中拥有该值的副本,而不是引用。如果允许您更改
item
中的值,则它不会反映在数组中的值上,因为它是一个全新的值。这就是为什么不允许这样做的原因。 如果要执行此操作,则必须按索引遍历数组,而不能使用临时变量。     
结构是值类型。 类是引用类型。 “ 3”构造使用IEnumerable数据类型元素的IEnumerator。如果发生这种情况,则变量在某种意义上是只读的,即您无法修改它们,并且由于您具有值类型,因此您无法修改其中包含的任何值,因为它共享相同的内存。 C#语言规范第8.8.4节:   迭代变量对应于只读局部变量,其范围扩展到嵌入式语句中 修复由结构引起的此使用类;
class MyStruct {
    public string Name { get; set; }
}
编辑:@崔鹏飞 如果您使用“ 5”隐式类型,则编译器很难为您提供帮助。如果您使用“ 6”,它将告诉您结构的情况,它是只读的。在类的情况下,对该项目的引用是只读的,因此您不能在循环内编写“ 7”,但可以更改其可变的属性。 您也可以使用(如果您想使用
struct
):
        MyStruct[] array = new MyStruct[] { new MyStruct { Name = \"1\" }, new MyStruct { Name = \"2\" } };
        for (int index=0; index < array.Length; index++)
        {
            var item = array[index];
            item.Name = \"3\";
        }
    
注意:根据亚当的评论,这实际上不是问题的正确答案/原因。不过,这仍然值得牢记。 从MSDN。使用枚举器时,您不能修改值,这实际上是foreach所做的。   枚举器可用于读取   集合中的数据,但它们   不能用来修改   基础集合。      枚举器只要保持有效   集合保持不变。如果   对集合进行了更改,   例如添加,修改或删除   元素,枚举数是   不可恢复的无效及其   行为是不确定的。     
将MyStruct设为类(而不是struct),就可以做到这一点。     
我认为您可以在下面找到答案。 C#中的Foreach结构怪异编译错误     
值类型按值调用。简而言之,当您评估变量时,将创建它的副本。即使允许这样做,您也将编辑副本,而不是原始的
MyStruct
实例。
foreach
在C#中是只读的。对于引用类型(类),这并没有太大变化,因为只有引用是只读的,因此仍然可以执行以下操作:
    MyClass[] array = new MyClass[] {
        new MyClass { Name = \"1\" },
        new MyClass { Name = \"2\" }
    };
    foreach ( var item in array )
    {
        item.Name = \"3\";
    }
但是,对于值类型(结构),整个对象是只读的,这将导致您遇到的情况。您无法在foreach中调整对象。     

要回复问题请先登录注册