更改结构列表中元素的值

我有一个结构列表,我想改变一个元素。 例如 :

MyList.Add(new MyStruct("john"); MyList.Add(new MyStruct("peter"); 

现在我想改变一个元素:

 MyList[1].Name = "bob" 

但是,每当我尝试这样做,我得到以下错误:

无法修改System.Collections.Generic.List.this [int]的返回值,因为它不是一个variables

如果我使用类的列表,问题不会发生。

我想答案与结构是一种价值types有关。

所以,如果我有一个结构列表,我应该把它们看作是只读的吗? 如果我需要更改列表中的元素,那么我应该使用类而不是结构?

 MyList[1] = new MyStruct("bob"); 

C#中的结构几乎总是被devise成不可变的(也就是说,一旦创build完成,就无法改变它们的内部状态)。

在你的情况下,你想要做的就是replace指定的数组索引中的整个结构,而不是试图只改变一个属性或字段。

不完全的。 将typesdevise为类或结构不应该由您需要将其存储在集合中驱动:)您应该查看所需的“语义”

你看到的问题是由于值types的语义。 每个值typesvariables/引用是一个新的实例。 当你说

 Struct obItem = MyList[1]; 

会发生什么情况是一个新的结构实例被创build,并且所有的成员被一个接一个地复制。 所以你有一个MyList [1]的克隆,即2个实例。 现在,如果您修改obItem,它不会影响原来的。

 obItem.Name = "Gishu"; // MyList[1].Name still remains "peter" 

现在忍受我在这里2分钟(这需要一段时间来吞咽下来..它为我做了:)如果你真的需要结构存储在一个集合,并修改你喜欢你在你的问题中指出,你将不得不你的结构暴露一个接口( 但是这将导致拳击 )。 然后,您可以通过引用装箱对象的接口引用来修改实际的结构。

以下代码片段说明了我刚才所说的内容

 public interface IMyStructModifier { String Name { set; } } public struct MyStruct : IMyStructModifier ... List<Object> obList = new List<object>(); obList.Add(new MyStruct("ABC")); obList.Add(new MyStruct("DEF")); MyStruct temp = (MyStruct)obList[1]; temp.Name = "Gishu"; foreach (MyStruct s in obList) // => "ABC", "DEF" { Console.WriteLine(s.Name); } IMyStructModifier temp2 = obList[1] as IMyStructModifier; temp2.Name = "Now Gishu"; foreach (MyStruct s in obList) // => "ABC", "Now Gishu" { Console.WriteLine(s.Name); } 

HTH。 好问题。
更新: @Hath – 你让我跑来检查我是否忽略了一些简单的东西。 (这将是不一致的,如果设置属性dont和方法没有 – NET的宇宙仍然平衡:)
Setter方法不起作用
obList2 [1]返回状态将被修改的副本。 列表中的原始结构保持不变。 所以Set-via-Interface似乎只能这样做。

 List<MyStruct> obList2 = new List<MyStruct>(); obList2.Add(new MyStruct("ABC")); obList2.Add(new MyStruct("DEF")); obList2[1].SetName("WTH"); foreach (MyStruct s in obList2) // => "ABC", "DEF" { Console.WriteLine(s.Name); } 

结构不是“不变的”。

真正的潜在问题是,结构是一个值types,而不是引用types。 所以当你从列表中取出一个“引用”到结构体时,它正在创build整个结构体的新副本。 因此,您对其进行的任何更改都将更改副本,而不是更改列表中的原始版本。

像安德鲁状态一样,你必须replace整个结构。 至于这一点,虽然我认为你必须问自己,为什么你使用一个结构(而不​​是一个类)。 确保你没有做这个过早的优化问题。

结构暴露了领域,或者允许通过属性设置者进行突变没有什么问题。 然而,为了响应方法或属性获取器而变异的构造是危险的,因为系统将允许在临时结构实例上调用方法或属性获取器; 如果方法或getter对结构进行更改,那么这些更改将最终被丢弃。

不幸的是,正如你注意到的,内置到.net中的集合在暴露包含在其中的值types对象时真的很虚弱。 你最好的select通常是做类似的事情:

   MyStruct temp = myList [1];
   temp.Name =“Albert”;
   myList [1] = temp;

有点讨厌,根本不用线程安全。 仍然是一个类types的列表,在做同样的事情可能需要:

   myList [1] .Name =“Albert”;

但也可能需要:

   myList [1] = myList [1] .Withname(“Albert”);

或者可能

   myClass temp =(myClass)myList [1] .Clone();
   temp.Name =“Albert”;
   myList [1] = temp;

或者其他一些变化。 一个人真的不能知道,除非检查myClass以及其他代码列表中的东西。 如果不检查程序集中没有访问权限的代码,就可能无法知道第一个表单是否安全。 相比之下,如果Name是MyStruct的一个公开的字段,那么无论MyStruct包含什么内容,或者在代码执行之前可能用myList做了什么其他事情,我给出的用于更新的方法都可以工作,或者他们可能期望跟着做吧。