C# Can';t改变结构';s成员在泛型集合中的值

C# Can';t改变结构';s成员在泛型集合中的值,c#,.net,struct,generic-list,generic-collections,C#,.net,Struct,Generic List,Generic Collections,想象一下这个结构: struct Person { public string FirstName { get; set; } public string LastName { get; set; } } 和以下代码: var list = new List<Person>(); list.Add(new Person { FirstName = "F

想象一下这个结构:

        struct Person
        {
             public string FirstName { get; set; }
             public string LastName { get; set; }
        }
和以下代码:

        var list = new List<Person>();
        list.Add(new Person { FirstName = "F1", LastName = "L1" });
        list.Add(new Person { FirstName = "F2", LastName = "L2" });
        list.Add(new Person { FirstName = "F3", LastName = "L3" });

        // Can't modify the expression because it's not a variable
        list[1].FirstName = "F22";

然而,当我试图在数组(如
Person[]
中更改它时,它没有出现任何错误。在使用泛型集合时,我的代码有任何问题吗?

当您通过
列表[]
索引器返回
结构时,它会返回条目的副本。因此,如果您在那里指定了
FirstName
,它将被丢弃。因此出现了编译器错误

将您的
人员
重写为参考类型
,或进行完全重新分配:

Person person = list[1];
person.FirstName = "F22";
list[1] = person;
一般来说,可变结构会带来这样的问题,这可能会让人头疼。除非你有一个很好的理由去使用它们,否则你应该强烈地考虑改变你的<代码>人<代码>类型。

按如下方式重做
结构:

    struct Person
    {
         private readonly string firstName;
         private readonly string lastName;
         public Person(string firstName, string lastName)
         {
             this.firstName = firstName;
             this.lastName = lastName;
         }
         public string FirstName { get { return this.firstName; } }
         public string LastName { get { return this.lastName; } }
    }
以下代码为:

    var list = new List<Person>();
    list.Add(new Person("F1", "L1"));
    list.Add(new Person("F2", "L2"));
    list.Add(new Person("F3", "L3"));

    // Can modify the expression because it's a new instance
    list[1] = new Person("F22", list[1].LastName);
var list=newlist();
添加(新人员(“F1”、“L1”);
添加(新人员(“F2”、“L2”);
添加(新人员(“F3”、“L3”);
//无法修改表达式,因为它是新实例
列表[1]=新人(“F22”,列表[1]。姓氏);

这是由于
struct
的复制语义造成的。使其不可变并在这些约束条件下工作,问题就会消失。

显然,问题的一部分仍然没有答案。
List
Person[]
之间有什么区别。就按索引获取元素而言,
List
调用indexer(方法),该方法返回值类型实例的副本,在相反的数组中,按索引返回的不是副本,而是指向索引处元素的托管指针(使用特殊IL指令)

当然,可变值类型是邪恶的,正如其他答案中提到的那样。看看这个简单的例子

var en = new {Ints = new List<int>{1,2,3}.GetEnumerator()};
while(en.Ints.MoveNext())
{
    Console.WriteLine(x.Ints.Current);
}
var en=new{Ints=new List{1,2,3}.GetEnumerator()};
while(en.Ints.MoveNext())
{
控制台写入线(x.Ints.Current);
}

惊讶?

为什么你的人没有类?拥有一个可变结构不是一个好主意-你不能使用类吗?试着看看这个-这是MSDN上的一个解释:-这是因为值类型是在赋值时复制的。。。当您从属性或索引器检索值类型时,您得到的是对象的副本,而不是对象本身的引用。对于完全相同的问题,已经有了很好的答案@Ginosaji您是如何知道的,如果
一般的经验法则是永远不要使用struct
?+1您介绍的读/修改/写回方法是编写此代码的最干净的方法,因为它使结构的哪些部分发生了更改变得非常明显。代码类似于list[1]=新人(“F22”,list[1]。姓氏)
不太清晰,更可能出错[例如,如果
Person
有一个可选的
MiddleName
参数,后面的代码会意外地将其清除--oops]。我不喜欢“不可变”方法,因为必须检查结构的代码才能知道
list[1]=新人(“F22”,list[1].LastName)的效果如何);是。例如,如果
Person
类包含一个
MiddleName
字段,该字段将由可选的
MiddleName
参数初始化,则上述语句将(可能意外地)擦除
列表[1]。MiddleName
。相反,如果
LastName
是一个公开字段,那么知道这一点就足以确定Chris Sinclair答案中的代码不会影响其他任何内容。
var en = new {Ints = new List<int>{1,2,3}.GetEnumerator()};
while(en.Ints.MoveNext())
{
    Console.WriteLine(x.Ints.Current);
}