在C#中创建对象的副本

afa*_*lek 150 c# reference object cloning

可能重复:
如何在.Net(特别是C#)中深层复制对象?

请看下面的代码(摘自C#书):

public class MyClass 
{
    public int val;
}
public struct myStruct 
{
    public int val;
}
public class Program 
{
    private static void Main(string[] args) 
    {
        MyClass objectA = new MyClass();
        MyClass objectB = objectA;

        objectA.val = 10;
        objectB.val = 20;

        myStruct structA = new myStruct();
        myStruct structB = structA;

        structA.val = 30;
        structB.val = 40;

        Console.WriteLine("objectA.val = {0}", objectA.val);
        Console.WriteLine("objectB.val = {0}", objectB.val);
        Console.WriteLine("structA.val = {0}", structA.val);
        Console.WriteLine("structB.val = {0}", structB.val);

        Console.ReadKey();
    }
}
Run Code Online (Sandbox Code Playgroud)

我知道它会产生下面的输出

objectA.val = 20
objectB.val = 20
structA.val = 30
structB.val = 40
Run Code Online (Sandbox Code Playgroud)

输出的最后两行我没有问题,但前两个告诉我objectAobjectB指向相同的内存块(因为在C#中,对象是引用类型).

问题是make如何制作objectB,objectA以便它指向内存中的不同区域.我理解,尝试分配他们的成员可能不起作用,因为这些成员也可能是参考.那么我该如何制作objectB一个完全不同的实体objectA呢?

谢谢

Bug*_*der 129

你可以这样做:

class myClass : ICloneable
{
    public String test;
    public object Clone()
    {
        return this.MemberwiseClone();
    }
}
Run Code Online (Sandbox Code Playgroud)

那么你可以做到

myClass a = new myClass();
myClass b = (myClass)a.Clone();
Run Code Online (Sandbox Code Playgroud)

NB MemberwiseClone()创建当前System.Object的浅表副本.

  • @Tom:-1评论(如果可以的话)是因为没有给出关于你的陈述的任何链接,因为ICloneable在[官方文档]中没有标记为过时(http://msdn.microsoft.com/library/system.icloneable (v = vs.110)的.aspx)).你更多的是谈论最好的实践,而不是官方弃用状态. (122认同)
  • -1没有提及浅/深克隆及其影响.没有这些信息,`MemberwiseClone()`很棘手. (35认同)
  • -1表示不提及ICloneable已弃用,不应使用. (8认同)
  • ...但这会产生与用户原始问题相同的结果.直接来自文档:"如果字段是值类型,则执行字段的逐位复制.如果字段是引用类型,则复制引用但引用的对象不是;因此,原始对象及其克隆引用同一个对象." https://msdn.microsoft.com/en-us/library/system.object.memberwiseclone(v=vs.110).aspx (4认同)
  • 嗯..它并没有被弃用,因此建议不要在公共 API 中使用,因为该 API 的使用者不清楚该 API 的实现,但对于内部使用或开源我认为没问题。他们声明的是“因为 Clone 的调用者不能依赖于执行可预测克隆操作的方法,我们建议不要在公共 API 中实现 ICloneable。” (2认同)

Hen*_*man 103

没有内置的方式.你可以让MyClass实现IClonable接口(但它有点不赞成)或只是编写自己的Copy/Clone方法.在任何一种情况下,您都必须编写一些代码.

对于大对象,您可以考虑序列化+反序列化(通过MemoryStream),只是为了重用现有代码.

无论采用何种方法,都要仔细考虑"副本"的含义.它应该有多深,是否有被禁止的Id字段等.

  • @LeeTaylor最后一点,Microsoft将[Obsolete](http://msdn.microsoft.com/en-us/library/system.obsoleteattribute%28v=vs.110%29.aspx)属性添加到类,结构,他们认为已弃用的枚举,委托,方法,构造函数,属性,字段,事件和接口导致编译器发出[CS0618](http://msdn.microsoft.com/en-us/library/x5ye6x1e.aspx)警告表示它确实已被弃用.`ICloneable`没有应用`Obsolete`属性. (48认同)
  • @LeeTaylor弃用意味着"表示不赞成",在软件环境中,弃用意味着表示不赞成使用,绝对.您不能拥有在公开时弃用的API,而不是私有的API.不推荐使用的API已弃用,除非绝对必要,否则不应使用.微软并没有说你绝对不应该使用它,因为它已被弃用(通常提供替代方案),它只是说避免在公共AP中使用它. (18认同)
  • 没关系,我找到了.在[.NET文档](http://msdn.microsoft.com/en-us/library/system.icloneable%28v=vs.110%29.aspx)中,他们"建议不要在公共API中实现ICloneable" .所以它没有被弃用,只是不建议使用. (12认同)
  • 对不起,我知道这是一个非常古老的帖子,但是你能提供一个文件的链接,说"ICloneable"被弃用了吗?我查看了.net 4.5的文档,而"ICloneable"没有提到任何关于被弃用的内容.如果是的话那我就想用别的东西. (5认同)
  • @LeeTaylor维基百科说:"弃用是一种应用于计算机软件功能,特征或实践的属性,表明应该避免(通常因为它被取代)"; 完全避免,避免基于可访问性而避免. (2认同)

小智 46

最简单的方法是在MyClass类中编写一个复制构造函数.

像这样的东西:

namespace Example
{
    class MyClass
    {
        public int val;

        public MyClass()
        {
        }

        public MyClass(MyClass other)
        {
            val = other.val;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

第二个构造函数只接受他自己类型的参数(您要复制的参数)并创建一个分配了相同值的新对象

class Program
{
    static void Main(string[] args)
    {
        MyClass objectA = new MyClass();
        MyClass objectB = new MyClass(objectA);
        objectA.val = 10;
        objectB.val = 20;
        Console.WriteLine("objectA.val = {0}", objectA.val);
        Console.WriteLine("objectB.val = {0}", objectB.val);
        Console.ReadKey();
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

objectA.val = 10

objectB.val = 20               
Run Code Online (Sandbox Code Playgroud)

  • 复制构造函数的问题在于,如果添加/删除字段,则还必须修改复制构造函数.这可能成为维护的噩梦.特别是对于具有许多字段的对象(50 +即DataContract). (12认同)
  • 这很好学习.不是为了练习. (6认同)
  • 我喜欢这个答案。Clone 方法返回类对象,因此它是一种构造函数。那么,为什么叫它Clone而不使用类名呢?那么,为什么会出现“维护噩梦”呢?如果您需要维护大量字段列表,这是最好的地方。关于“已弃用”:没有人强迫您继承 IClonable 接口。您可以在没有接口的情况下实现 Clone() 方法。但毕竟我会在我的项目中实现复制构造函数。 (2认同)

jge*_*ina 9

关于这个问题已经有了问题,你或许可以阅读它

深度克隆对象

例如,Java中没有Clone()方法,但你可以在你的clases中包含一个拷贝构造函数,这是另一个好方法.

class A
{
  private int attr

  public int Attr
  {
     get { return attr; }
     set { attr = value }
  }

  public A()
  {
  }

  public A(A p)
  {
     this.attr = p.Attr;
  }  
}
Run Code Online (Sandbox Code Playgroud)

这是一个例子,在构建新对象时复制成员'Attr'.