Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/299.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在C#中,为什么可以';我是否修改foreach循环中值类型实例的成员?_C#_Foreach_Immutability_Value Type - Fatal编程技术网

在C#中,为什么可以';我是否修改foreach循环中值类型实例的成员?

在C#中,为什么可以';我是否修改foreach循环中值类型实例的成员?,c#,foreach,immutability,value-type,C#,Foreach,Immutability,Value Type,我知道值类型应该是不可变的,但这只是一个建议,不是规则,对吗? 那么为什么我不能这样做: struct MyStruct { public string Name { get; set; } } public class Program { static void Main(string[] args) { MyStruct[] array = new MyStruct[] { new MyStruct { Name = "1" }, new MySt

我知道值类型应该是不可变的,但这只是一个建议,不是规则,对吗? 那么为什么我不能这样做:

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();
    }
}
struct MyStruct
{
公共字符串名称{get;set;}
}
公共课程
{
静态void Main(字符串[]参数)
{
MyStruct[]array=newmystruct[]{newmystruct{Name=“1”},newmystruct{Name=“2”};
foreach(数组中的变量项)
{
item.Name=“3”;
}
//for(int i=0;i
代码中的foreach循环不编译,而注释的for循环工作正常。错误消息:

无法修改“item”的成员,因为它是“foreach迭代变量”

为什么会这样?

注意:根据亚当的评论,这实际上不是问题的正确答案/原因。不过,这仍然是值得记住的事情

从。使用枚举器时不能修改值,这基本上就是foreach所做的

枚举数可用于读取 集合中的数据,但它们 无法用于修改 基础集合

只要 收藏保持不变。如果 对集合进行了更改, 例如添加、修改或删除 元素,枚举数为 不可恢复的无效及其后果 行为是未定义的


这是一个建议,从某种意义上说,没有什么能阻止你违反它,但它确实应该比“这是一个建议”更重要。例如,你在这里看到的原因

值类型将实际值存储在变量中,而不是引用中。这意味着您在数组中有该值,并且在
项中有该值的副本,而不是引用。如果允许您更改
中的值,它将不会反映在数组中的值上,因为它是一个全新的值。这就是为什么这是不允许的


如果您想这样做,您必须按索引循环数组,而不是使用临时变量。

将MyStruct设置为类(而不是struct),您就可以做到这一点。

我认为您可以在下面找到anwser


因为foreach使用枚举器,枚举器不能更改基础集合,但是可以更改集合中对象引用的任何对象。这就是值和引用类型语义发挥作用的地方

在引用类型(即类)上,集合存储的所有内容都是对对象的引用。因此,它实际上从未接触过对象的任何成员,也不关心它们。对对象的更改不会影响集合

另一方面,值类型将其整个结构存储在集合中。在不更改集合并使枚举数无效的情况下,无法接触其成员

此外,枚举数返回集合中值的副本。在ref类型中,这意味着什么也没有。引用的副本将是同一个引用,并且您可以在更改扩展到范围之外的情况下以任何方式更改引用对象。另一方面,在值类型上,意味着您得到的只是对象的一个副本,因此对所述副本的任何更改都不会传播。

值类型是。简言之,当您计算变量时,会生成它的副本。即使允许这样做,您也将编辑一个副本,而不是
MyStruct
的原始实例

。对于引用类型(类),这不会有太大变化,因为只有引用是只读的,所以仍然允许您执行以下操作:

    MyClass[] array = new MyClass[] {
        new MyClass { Name = "1" },
        new MyClass { Name = "2" }
    };
    foreach ( var item in array )
    {
        item.Name = "3";
    }

但是,对于值类型(struct),整个对象是只读的,这会导致您的体验。无法在foreach中调整对象。

结构是值类型
类是引用类型

ForEach
构造使用IEnumerable数据类型元素的IEnumerator。当这种情况发生时,变量在某种意义上是只读的,您不能修改它们,并且因为您有值类型,所以您不能修改包含的任何值,因为它共享相同的内存

C#lang规范第8.8.4节:

迭代变量对应于一个只读局部变量,其作用域扩展到嵌入语句

以固定此使用类而不是结构

class MyStruct {
    public string Name { get; set; }
}
编辑:@崔鹏飞

如果您使用
var
implicit类型,编译器很难帮助您。如果您使用
MyStruct
,它会告诉您在结构的情况下,它是只读的。对于类,对项的引用是只读的,因此不能写入
item=null内部循环,但您可以更改它的属性,这些属性是可变的

您还可以使用(如果您喜欢使用
struct
):

MyStruct[]array=newmystruct[]{newmystruct{Name=“1”},newmystruct{Name=“2”};
for(int index=0;index
虽然看起来像这样,但这不是问题所在。您当然可以修改通过
foreach
获得的实例的属性或字段值(这不是MSDN文章所讨论的),因为这是在修改集合的成员,而不是集合本身。这里的问题是值类型和引用类型的语义。事实上,我刚刚读了你的答案,并意识到了我的错误。我会留下这个,因为它仍然有点有用。谢谢,但我不认为这是原因。因为我试图更改集合元素的一个成员,而不是集合本身。如果我
        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";
        }