无法更改generics集合中struct的成员值
想象一下这个struct
:
struct Person { public string FirstName { get; set; } public string LastName { get; set; } }
以下代码:
var list = new List(); 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";
当我想更改Property
的值时,它会给我以下错误:
Can't modify the expression because it's not a variable
然而,当我试图在像Person[]
这样的数组中更改它时,它没有任何错误。当使用generics集合时,我的代码有什么问题吗?
通过List[]
索引器返回struct
,它返回条目的副本 。 因此,如果您在那里分配了FirstName
,它就会被丢弃。 因此编译错误。
将您的Person
重写为引用类型class
,或者执行完全重新分配:
Person person = list[1]; person.FirstName = "F22"; list[1] = person;
一般来说,可变结构带来了诸如此类的问题,这些问题可能导致头痛。 除非您有充分的理由使用它们,否则您应该强烈考虑更改您的Person
类型。
为什么可变结构“邪恶”?
显然问题的一部分仍然没有答案。 List
和Person[]
之间有什么区别。 在按索引获取元素的方面, List
调用返回值类型实例的副本的索引器(方法),在索引的相反数组中返回不是副本但是在索引处指向元素的托管指针(使用特殊的IL指令ldelema )。
当然,其他答案中提到的可变价值类型是邪恶的。 看一下这个简单的例子。
var en = new {Ints = new List{1,2,3}.GetEnumerator()}; while(en.Ints.MoveNext()) { Console.WriteLine(x.Ints.Current); }
惊讶吗?
像这样重做你的struct
:
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(); 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);
这是由于struct
的复制语义。 使它不可变并在这些约束下工作,问题就消失了。