如何复制一个对象?

5
我创建了一个名为Colors的类。我正在设置Colors对象上的某些属性,并将其设置为Session变量。当我在另一个页面访问Session变量时,我注意到如果我更改下面objColors上的属性,它会更改Session并且不会保留原始属性,而这正是我想要的结果。以下是一个示例:
Session["Colors"] = Colors;

Colors objColors = Session["Colors"];

//If I change objColors, it changes the Session.  I don't want this to happen.

有没有更好的方法来保留原始属性?为什么会这样做?

10个回答

7
为Colors创建一个复制构造函数。然后,按照以下步骤操作。
Colors objColors = new Colors((Colors)Session["Colors"]);

在你的新构造函数中,只需复制所需的值并执行其他与构造函数相关的操作。
在你的代码中发生的是,你得到了一个指向Colors对象的指针。Session["Colors"]和objColors指向内存中的同一对象,因此当你修改其中一个时,更改会反映在两个对象中。你需要一个全新的Colors对象,其值从objColors或Session["Colors"]初始化。
编辑:拷贝构造函数可能如下所示:
public Colors(Colors otherColors)
{
    this.privateVar1 = otherColors.privateVar1;
    this.publicVar2 = otherColors.publicVar2;
    this.Init();
}

你能更具体地说明如何创建一个复制构造函数吗? - Xaisoft
@Xaisoft,你的工作是创建复制逻辑。这只是将旧对象上的每个值应用到新对象上的问题。 - Rex M

3
您可以实现自己的克隆方法来制作对象的“副本”。之所以会发生这种情况,是因为Colors objColors = Session["Colors"];的赋值是一个引用赋值,这是有意设计的。您所做的只是创建一个本地范围引用到已经存在的对象。请查看IClonable以获取实际的对象克隆方法。这并非必要,您也可以实现自己的复制方法。根据您的对象深度,您可能还需要查看MemberwiseClone

3
尝试使用拷贝构造函数。
示例:链接

2
该对象是通过引用而不是值进行访问。在这里查看。有几种方法可以改变这种情况。请查看该网站以获取详细信息。

2
其他答案都提到了克隆。另一种可能适用于您特定情况的替代方法是使您的类型不可变。以前会改变您的类型的操作现在将返回类型的新实例,从原始对象中获取数据并进行适当的更改,同时保留原始实例不变。例如,这就是 String 类采用的方法。
当然,您仍然需要编写适当的代码来复制实例内的数据,但是与该类型一起工作的代码可能会更简单。
这在您的情况下可能不适用,但至少可以考虑一下这种技术。

1

默认情况下,没有内置的方法和实现方式可以实现对象的复制,但是您可以通过实现ICloneable接口并调用MemberwiseClone方法来实现。这只适用于浅层复制--如果您的对象包含其他对象,则还需要对它们进行克隆。一个简单的实现方式如下:

public class Bla : ICloneable
{
    string _someFieldToClone;

    object ICloneable.Clone()
    {
        return this.Clone();
    }

    public Bla Clone()
    {
        return (Bla)MemberwiseClone();
    }
}

@erikkallen:不是的。http://msdn.microsoft.com/zh-cn/library/system.icloneable.aspx - Zhaph - Ben Duguid

1

这是因为你实际上并没有复制对象,而是复制了一个指向该对象的引用。在C#中,你可以使用二进制序列化轻松地进行深度复制:

  public static MemoryStream Serialize(object data)
    {

        MemoryStream streamMemory = new MemoryStream();
        BinaryFormatter formatter = new BinaryFormatter();
        formatter.AssemblyFormat = FormatterAssemblyStyle.Simple;

        formatter.Serialize(streamMemory, data);

        return streamMemory;


    }



   public static Object Deserialize(MemoryStream stream)
    {

        BinaryFormatter formatter = new BinaryFormatter();
        formatter.AssemblyFormat = FormatterAssemblyStyle.Simple;
        return formatter.Deserialize(stream);

    }

你可以调用这两个方法,第一个方法接受一个对象并将其数据写入MemoryStream。然后,您可以调用Deserialize方法来获取基于该数据的新对象副本。
最后一件事是,您的对象需要是可序列化的,通过在每个对象上放置Serializable属性来实现。

0

由于Colors类是您创建的,我假设您对面向对象编程还比较新,因此您可以像Stuart所说的那样使用以下类似的代码创建一个复制构造函数:

public class Colors
{
  public int Property1;
  public int Property2;
  public int Property3;
  public Colors()
  {
    //Do your regular constructor here
  }
  public Colors(Colors colorToCopy)
  {
    this.Property1 = colorToCopy.Property1;
    this.Property2 = colorToCopy.Property2;
    this.Property3 = colorToCopy.Property3;
  }
}

通过序列化进行克隆是一种更通用的解决方案,可以确保深度复制(这意味着它将复制属性副本以及属性的属性),但在我看来有点难以理解。使用此解决方案,如果属性不是原始数据类型,则除非您明确地将它们复制,否则不会复制这些属性。


0
这是因为objColors和Session["Colors"]对象是同一个对象。如果您想存储副本并保留原始副本,您需要使您的对象可克隆。
请参阅以下SO Q&A以获取正确方向的指针: 深度克隆对象

0

当您有一个变量并将会话值分配给它时,您将获得对它们两个的指针。

Colors objColors = (Colors)Session["Colors"];

Session["Colors"] 和 objColors 指向内存中的同一对象,因此当您在其中一个对象中进行更改时,更改会反映在另一个对象中。

如果您想要独立性,则需要获取对象的 深层副本。您必须为发票及其所有相关类实现 IClonable 接口:

public class Colors: IClonable
{
  public int Red;
  public int Green;
  public int Blue;

  public object Clone()
  {
    return this.MemberwiseClone();
  }
}

现在你拥有了一个真正的发票对象深度复制。

Colors objColors = (Colors)((Colors)Session["Colors"]).Clone();

更多信息请参考:使用IClonableMemberwiseClone进行对象克隆,具体取决于您的对象深度。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接