更改结构列表中元素的值
我有一个结构列表,我想更改一个元素。例如 :
MyList.Add(new MyStruct("john"); MyList.Add(new MyStruct("peter");
现在,我想更改一个元素:
MyList[1].Name = "bob"
但是,每当我尝试执行此操作时,都会出现以下错误:
Cannot modify the return value of System.Collections.Generic.List.this[int]‘ because it is not a variable
如果我使用类列表,则不会发生此问题。
我猜答案与结构是值类型有关。
因此,如果我有一个结构体列表,是否应该将它们视为只读结构?如果我需要更改列表中的元素,那么应该使用类而不是结构?
解决方案
回答
MyList[1] = new MyStruct("bob");
Csho中的struct几乎总是被设计成不可变的(也就是说,一旦它们被创建,就无法改变其内部状态)。
在情况下,我们要做的是替换指定数组索引中的整个结构,而不是尝试仅更改单个属性或者字段。
回答
不完全的。将类型设计为类或者结构不应由将其存储在集合中的需求所驱动:)我们应该查看所需的"语义"
我们看到的问题是由于值类型语义所致。每个值类型变量/引用都是一个新实例。当你说
Struct obItem = MyList[1];
发生的情况是创建了该结构的新实例,并且所有成员都被一个一个地复制。这样我们就可以克隆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,我们让我跑步检查我是否忽略了这么简单的内容。 (如果设置器属性和方法没有使.Net Universe仍然保持平衡,那将是不一致的:)
设置器方法不起作用
obList2 [1]返回其状态将被修改的副本。列表中的原始结构保持不变。因此,"通过接口设置"似乎是唯一的方法。
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); }
回答
结构不是"不可变的"不是很多。
真正的根本问题是结构是值类型,而不是引用类型。因此,当我们从列表中拉出对该结构的"引用"时,它将创建整个结构的新副本。因此,我们对其进行的任何更改都将更改副本,而不是列表中的原始版本。
像安德鲁州一样,我们必须替换整个结构。尽管如此,我认为我们必须问自己,为什么首先要使用结构(而不是类)。确保我们没有围绕过早的优化问题进行操作。