可以';t更改结构';s成员在泛型集合中的值
本文关键字:集合 泛型 结构 可以 成员 | 更新日期: 2023-09-27 17:59:52
想象一下这个struct
:
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";
当我想更改Property
的值时,它会给我以下错误:
Can't modify the expression because it's not a variable
然而,当我试图在Person[]
之类的数组中更改它时,它没有任何错误。与泛型集合一起使用时,我的代码有问题吗?
通过List[]
索引器返回struct
时,它会返回条目的副本。所以,如果你把FirstName
分配到那里,它就会被扔掉。因此出现编译器错误。
要么将Person
重写为引用类型class
,要么进行完全重新分配:
Person person = list[1];
person.FirstName = "F22";
list[1] = person;
一般来说,可变结构会带来这样的问题,这些问题可能会让人头疼。除非您有充分的理由使用它们,否则您应该强烈考虑更改Person
类型。
为什么可变结构是"邪恶的"?
显然,问题的一部分仍未得到解答。List<Person>
和Person[]
有什么区别。在按索引获取元素方面,List
调用indexer(方法),该方法返回值类型实例的副本,在相反的按索引数组中,返回的不是副本,而是指向索引处元素的托管指针(使用特殊的IL指令ldelema)。
当然,正如其他答案中提到的那样,可变值类型是邪恶的。看看这个简单的例子。
var en = new {Ints = new List<int>{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<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);
这是由于struct
的复制语义。让它不可变,并在这些约束条件下工作,问题就会消失。