我有一个放入 a 的结构List<T>
,我想在该结构中的特定位置编辑一些值。如果不复制结构、编辑副本和替换列表中的条目,这是否可能?
3 回答
不,要做到这一点,您需要引用List
/未提供的内部数组元素IList
。
如果必须,您可以使用不安全的代码和数组来做到这一点。
来自 J.Richter 的“CLR via C#”,第 3 版:
值类型应该是不可变的:也就是说,它们不应该定义任何修改任何类型的实例字段的成员。事实上,我建议将值类型的字段标记为只读,以便在您不小心编写尝试修改字段的方法时编译器会发出错误。
...
请记住,值类型和引用类型具有非常不同的行为,具体取决于它们的使用方式。
考虑这段代码:
public interface IChangeStruct
{
int Value { get; }
void Change(int value);
}
public struct MyStruct : IChangeStruct
{
int value;
public MyStruct(int _value)
{
value = _value;
}
public int Value
{
get
{
return value;
}
}
public void Change(int value)
{
this.value = value;
}
}
它的用法:
static void Main(string[] args)
{
MyStruct[] l = new MyStruct[]
{
new MyStruct(0)
};
Console.WriteLine(l[0].Value);
l[0].Change(10);
Console.WriteLine(l[0].Value);
Console.ReadLine();
}
输出是:
0 10
所以它可以满足您的需求。
但是,这同样不适用于List<T>
. 我猜是Alexei Levenkov提到的原因。因此,我强烈建议您更改struct
为class
如果所讨论的类型不是每个实例不可变的。
您最好的选择可能是让您的结构直接公开其字段,然后使用如下代码:
var temp = myList[3]; 温度.X += 4; 我的列表 [3] = 温度;
我认为 .net 未能提供任何更新列表项的方法是 .net 的一个重大弱点,但在希望表示不应“附加”到任何其他此类组的一小组正交值(例如点中的坐标,矩形的原点和大小等)。结构应该是“不可变”的概念已被重复为咒语很长一段时间,但这并不意味着它是好的建议。这样的概念主要源于两件事:
- 在其构造函数之外的任何成员中修改 `this` 的结构是古怪的。这种怪癖过去(并且在某种程度上仍然如此)适用于属性设置器,但不适用于直接公开其字段的结构。因为微软将所有结构字段都包装在属性中,这意味着虽然可变结构如果暴露了字段就可以具有合理的语义,但它们最终会产生古怪的语义。微软随后将古怪的语义归咎于结构是可变的这一事实,而不是不必要地用属性包装字段。
- 有些人喜欢将 .net 建模为只有一种对象,而不是将值类型和引用类型作为不同类型的实体。所谓的“不可变”值类型的行为与引用类型的行为非常接近,以至于它们可以假装它们是同一事物,而易于可变的值类型的行为则大不相同。实际上,理解暴露字段值类型的行为比理解所谓的“不可变”值类型与引用类型行为不同的所有极端情况更容易,如果不理解前者,就不可能理解后者。请注意,虽然值类型可能假装是不可变的,但实际上不存在不可变值类型这样的东西。
实际上,如果一个类型应该代表一小组正交值,那么暴露字段结构是完美的选择。即使必须使用上面显示的笨重代码来更新 a 中项目的字段List<structType>
,它也比使用类类型或所谓的“不可变”结构的任何替代方法要好。知道这myList
是一个带有暴露字段的结构X
就足以完全理解上面的代码。如果使用类或“不可变”结构,唯一的远程选择是myList[3] = myList[3].WithX(myList[3].X + 4);
,但这需要相关类型提供WithX
方法(并且可能是WithWhatever()
每个字段的方法)。这样的方法将增加许多倍的代码量,人们必须阅读以确定一个方法实际上会做什么(人们可能期望这WithX
将返回一个与旧实例相同的新实例,除了 的值X
,但是在阅读所有涉及的代码之前,人们不会知道;相比之下,知道这X
是结构类型的公开字段就足以知道上面的代码会做什么。