你如何制作一个对象的副本?

时间:2009-03-10 21:36:09

标签: c# asp.net

我创建了一个名为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 个答案:

答案 0 :(得分:7)

为Colors创建一个复制构造函数。然后,这样做。

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

在新的构造函数中,只需复制所需的值并执行与构造函数相关的其他操作。

您的代码中发生的事情是您正在向Colors对象获取 指针 。 Session [“Colors”]和objColors指向内存中的同一个对象,因此当您修改一个对象时,更改会反映在两者中。你想要一个品牌spankin'新的Colors对象,其值从objColors或Session [“Colors”]初始化。

编辑:复制构造函数可能如下所示:

public Colors(Colors otherColors)
{
    this.privateVar1 = otherColors.privateVar1;
    this.publicVar2 = otherColors.publicVar2;
    this.Init();
}

答案 1 :(得分:3)

您可以实现自己的克隆方法来制作对象的“副本”。之所以发生这种情况,是因为Colors objColors = Session["Colors"];的赋值是一个引用赋值,这是设计的。您所做的只是对已存在的对象进行局部范围引用。查看IClonable进行实际对象克隆。您无需实现自己的复制方法。您可能还需要查看MemberwiseClone,具体取决于对象的深度。

答案 2 :(得分:3)

尝试复制构造函数。

示例:link

答案 3 :(得分:2)

通过引用而不是按值访问对象。 See here。有几种方法可以改变这种情况。查看网站了解详细信息。

答案 4 :(得分:2)

其他答案都建议以某种方式进行克隆。可能适合您的特定情况的替代方法是使您的类型不可变。之前改变您的类型的操作现在将返回该类型的新实例,从原始对象获取数据并进行适当的更改,并使原始实例保持原样。例如,这是String类所采用的方法。

当然,您仍然必须编写适当的代码来复制实例中的数据 - 但使用该类型的代码最终可能会更简单。

这可能不适合你的情况,但这是一种至少要考虑的技术。

答案 5 :(得分:1)

默认情况下没有内置和实现的方法,但您可以通过实现ICloneable并调用MemberwiseClone来实现。这只适用于浅拷贝 - 如果你的对象包含其他对象,你也需要克隆它们。一个简单的实现是:

public class Bla : ICloneable
{
    string _someFieldToClone;

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

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

答案 6 :(得分: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,方法是在每个对象上放置Serializable属性。

答案 7 :(得分:0)

这是因为objColors和Session [“Colors”]对象是同一个对象。如果您想存储副本并保留原件,则需要使对象可以克隆。

请参阅以下SO Q& A,了解正确方向的指针:

Deep cloning objects

答案 8 :(得分:0)

由于类Color是你创建的,我认为你是面向对象编程的新手,你可以像Stuart一样创建一个类似于以下内容的副本constructor:

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;
  }
}

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

答案 9 :(得分:0)

当你有一个变量并为其分配会话值时,你会得到一个指向它们的指针。

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

Session [“Colors”]和objColors指向内存中的同一个对象,然后当你在一个对象中进行更改时,更改会反映在两者中。

如果您想要独立,需要获得对象的 Deep Copy 。您必须为Invoice及其所有相关类实现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进行对象克隆,具体取决于对象的深度。