更改结构列表中元素的值
我有一个结构列表,我想改变一个元素。 例如 :
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做了什么其他事情,我给出的用于更新的方法都可以工作,或者他们可能期望跟着做吧。