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);
}