C#从Array返回对象会留下指向Array项的指针

时间:2010-01-13 09:48:20

标签: c# .net .net-3.5 pointers c#-3.0

我有一个List<T>,我会执行以下操作:

var myObj = List[2];  //Return object at position 2
myObj.Name = "fred";  //If you look at List[2] its name has changed to fred

我尝试了以下操作,但它仍然更新列表中的项目

var newObj = new MyObj();
var myObj = List[2];  //Return object at position 2
newObj = myObj;
newObj.Name = "fred";  //If you look at List[2] its name has still changed to fred

如何避免此指针保留,以便我可以在不更新列表的情况下更新属性?

5 个答案:

答案 0 :(得分:3)

您可以使用ICloneable方法使对象实现MemberwiseClone界面,然后:

var myObj = List[2];
var newObj = myObj.Clone();
newObj.Name = "fred";

更新:

正如评论部分所指出的,不建议实现ICloneable接口,因为它没有指定它是执行浅或深克隆。只需在您的班级中添加Clone方法即可。

答案 1 :(得分:2)

这与数组或列表本身无关 - 它只是引用类型的工作方式。这是一个更简单的演示:

MyObj a = new MyObj();
MyObj b = a;
a.Name = "Test";
Console.WriteLine(b.Name); // Will print "Test"

列表和数组的工作方式相同 - 存储的值将是引用(假设它们是引用类型值),而不是对象本身的数据。

您可能需要阅读我的article about reference types and value types以获取更多信息。

正如其他人所说,如果你真的想要独立的对象,你需要克隆它们。我建议尝试以另一种方式围绕这个设计,但是,个人。

答案 2 :(得分:1)

你究竟想做什么?是否要更新列表中对象的属性,或者是否需要另一个对象,该对象是列表中对象的副本,但名称不同?

作为提供克隆方法的替代方法,您可以提供复制构造函数。

var newObj = new MyObj(List[2]);
newObj.Name = "fred";

您可以在一次通话中执行此操作,具体取决于您要执行的操作。如果您知道您总是要创建一个只有不同名称的副本,那么您可能想要执行类似

的操作
var newObj = new MyObj(List[2],"fred");

并在施工期间设置名称。

但如果没有以某种方式明确创建新对象,则无法执行所需操作。

请注意,如果你的对象有一个属于另一个对象的属性,你可能会遇到“深层复制”问题(这就是为什么不鼓励IClonable),就像你需要复制那个对象时那样做你newObj?您是否只提供对同一实例的引用?或者你也复制它?如果该对象没有克隆方法/复制构造函数,那么该怎么办?

答案 3 :(得分:1)

乔恩。在C#中,数组中的项目是“通过引用”存储的,这意味着您不会在数组中保存对象的副本,而是包含对它的引用(“指针”)。当您检索要检索引用的元素时,现在您有两个对同一对象的引用。由于在更新值时只有一个对象,因此两个引用都会“看到”新值。为避免这种情况,您需要复制实例,并且有几种方法可以执行此操作。正如Darin所提到的,你可以让对象实现ICloneable,你也可以和对象实现一个复制构造函数,或者你可以创建一个新对象并从'旧'数据中填充新对象。但是,请注意存在问题,主要问题是“深层复制”。如果您的对象包含对其他对象的引用,您如何复制它们?你复制了参考文献,或者你追溯参考文献和'深层复制'那些参考文献。对此没有单一的答案,只有经典的“它取决于!”

答案 4 :(得分:1)

很棒的评论,谢谢,但这是我实施的:

public MyObj Clone()
    {
        BinaryFormatter bFormatter = new BinaryFormatter();
        MemoryStream stream = new MemoryStream();
        bFormatter.Serialize(stream, this);
        stream.Seek(0, SeekOrigin.Begin);
        MyObj newObj = (MyObj)bFormatter.Deserialize(stream);
        return newObj;
    }