我有一个结构列表,我想改变一个元素.例如 :
MyList.Add(new MyStruct("john"); MyList.Add(new MyStruct("peter");
现在我想改变一个元素:
MyList[1].Name = "bob"
但是,每当我尝试这样做时,我都会收到以下错误:
无法修改System.Collections.Generic.List.this [int]'的返回值,因为它不是变量
如果我使用类列表,则不会出现问题.
我想答案与结构是一种值类型有关.
那么,如果我有一个结构列表,我应该将它们视为只读吗?如果我需要更改列表中的元素,那么我应该使用类而不是结构?
不完全的.将类型设计为类或结构不应该由您将其存储在集合中的需要驱动:)您应该查看所需的"语义"
您看到的问题是由于值类型语义.每个值类型变量/引用都是一个新实例.当你说
Struct obItem = MyList[1];
会发生什么是创建结构的新实例并逐个复制所有成员.这样你就有了MyList [1]的克隆,即2个实例.现在,如果您修改obItem,它不会影响原始.
obItem.Name = "Gishu"; // MyList[1].Name still remains "peter"
现在忍受我2分钟(这需要一段时间吞下去..它对我来说:)如果你真的需要结构存储在一个集合中并像你在问题中指出的那样修改,你将不得不制作你的struct暴露一个接口(但这会导致拳击).然后,您可以通过接口引用修改实际结构,该引用引用盒装对象.
以下代码片段说明了我刚才所说的内容
public interface IMyStructModifier { String Name { set; } } public struct MyStruct : IMyStructModifier ... List
HTH.好问题.
更新: @Hath - 你让我跑去检查我是否忽略了那么简单的事情.(如果不使用setter属性并且方法没有 - 这将是不一致的.Net Universe仍然是平衡的:)
Setter方法不起作用
obList2 [1]返回其状态将被修改的副本.列表中的原始结构保持不变.所以Set-via-Interface似乎只是这样做的方法.
ListobList2 = new List (); 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); }
MyList[1] = new MyStruct("bob");
C#中的结构应该几乎总是被设计为不可变的(也就是说,一旦创建它们就无法改变它们的内部状态).
在您的情况下,您要做的是替换指定数组索引中的整个结构,而不是尝试仅更改单个属性或字段.
并不是说结构是"不可变的".
真正的根本问题是结构是值类型,而不是引用类型.因此,当您从列表中提取结构的"引用"时,它将创建整个结构的新副本.因此,您对其所做的任何更改都会更改副本,而不是列表中的原始版本.
像Andrew所说,你必须替换整个结构.虽然我认为你必须问自己为什么你首先使用结构(而不是类).确保您没有围绕过早的优化问题来解决这个问题.
具有暴露字段或允许通过属性设置器进行突变的结构没有错。但是,响应于方法或属性获取器而变异的结构是危险的,因为系统允许在临时结构实例上调用方法或属性获取器。如果方法或获取方法对结构进行了更改,则这些更改最终将被丢弃。
不幸的是,正如您所注意到的,.net内置的集合在暴露其中包含的值类型对象方面确实微不足道。您最好的选择通常是执行以下操作:
MyStruct temp = myList [1]; temp.Name =“阿尔伯特”; myList [1] = temp;
有点烦人,根本不是线程安全的。仍然是对类类型列表的一种改进,在这种情况下,可能需要执行以下操作:
myList [1] .Name =“ Albert”;
但它可能还需要:
myList [1] = myList [1] .Withname(“ Albert”);
或许
myClass temp =(myClass)myList [1] .Clone(); temp.Name =“阿尔伯特”; myList [1] = temp;
或其他一些变化。除非一个人检查了myClass以及将这些东西放在列表中的其他代码,否则,一个人实际上是不会知道的。如果不检查无法访问的程序集中的代码,则很可能无法知道第一种形式是否安全。相比之下,如果Name是MyStruct的公开字段,那么我提供的用于更新它的方法将起作用,而不管MyStruct包含的内容是什么,也不管代码执行前myList可能执行的其他操作或期望的代码是什么之后再做。