在 C# 中创建对象的副本 [重复]

Posted

技术标签:

【中文标题】在 C# 中创建对象的副本 [重复]【英文标题】:Creating a copy of an object in C# [duplicate] 【发布时间】:2011-09-28 00:13:06 【问题描述】:

请看下面的代码(摘自一本 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();
    

我知道它会产生以下输出:

objectA.val = 20
objectB.val = 20
structA.val = 30
structB.val = 40

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

问题是如何使objectB 成为objectA 的副本,以便它指向内存中的不同区域。我知道尝试分配他们的成员可能不起作用,因为这些成员也可能是参考。那么我该如何让objectB 成为与objectA 完全不同的实体呢?

【问题讨论】:

这可能会有所帮助:***.com/questions/129389/… 字符串 json = Newtonsoft.Json.JsonConvert.SerializeObject(myobject); myObjType rCopy = Newtonsoft.Json.JsonConvert.DeserializeObject(json); @hal9000 我使用了序列化/反序列化方法。下面的克隆解决方案有缺点吗?似乎是一个直截了当的解决方案。 我没有遇到任何不利因素。但是我不能评论下面的克隆解决方案。我没试过 @SiavashGhanbari 那肯定没有帮助。这个问题仍然悬而未决。此特定问题已被标记为重复。这个问题是重复的。 【参考方案1】:

没有内置方法。您可以让 MyClass 实现 IClonable 接口(但它已被弃用)或者只是编写您自己的 Copy/Clone 方法。无论哪种情况,您都必须编写一些代码。

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

无论采用何种方法,请仔细考虑“副本”的确切含义。它应该走多深,是否有要排除的 Id 字段等。

【讨论】:

很抱歉,我知道这是一篇很老的帖子,但您能否提供一个指向已弃用 ICloneable 的文档的链接?我查看了 .net 4.5 的文档,ICloneable 没有说任何关于被弃用的信息。如果是的话,我想用别的东西。 没关系,我找到了。在.NET documentation 中,他们“建议不要在公共 API 中实现 ICloneable”。所以它没有被弃用,只是不推荐使用。 @LeeTaylor Deprecated 表示“表示不赞成”,在软件的上下文中,deprecated 表示完全反对使用。您不能拥有一个在公共而不是私有时被弃用的 API。已弃用的 API 已弃用,除非绝对必要,否则根本不应使用。 Microsoft 并没有说你绝对不应该使用它,因为它已被弃用(通常提供替代方案),它只是说避免在公共 API 中使用它。 @LeeTaylor Wikipedia 说“弃用是一种应用于计算机软件功能、特征或实践的属性,表明应该避免使用它(通常是因为它被取代了)”;避免就像完全避免,而不是基于可访问性而避免。 @LeeTaylor 最后一点,Microsoft 将Obsolete 属性添加到他们认为已弃用的类、结构、枚举、委托、方法、构造函数、属性、字段、事件和接口,这会导致编译器发出CS0618 警告,表明它确实已被弃用。 ICloneable 没有应用 Obsolete 属性。【参考方案2】:

已经有一个关于这个的问题,你也许可以阅读它

Deep cloning objects

没有 Clone() 方法,因为它存在于 Java 中,但你可以在你的类中包含一个复制构造函数,这是另一种好方法。

class A

  private int attr

  public int Attr
  
     get  return attr; 
     set  attr = value 
  

  public A()
  
  

  public A(A p)
  
     this.attr = p.Attr;
    

这是一个示例,在构建新对象时复制成员“Attr”。

【讨论】:

【参考方案3】:

你可以这样做:

class myClass : ICloneable

    public String test;
    public object Clone()
    
        return this.MemberwiseClone();
    

那你就可以了

myClass a = new myClass();
myClass b = (myClass)a.Clone();

注意MemberwiseClone() 创建当前 System.Object 的浅表副本。

【讨论】:

-1 未提及浅/深克隆及其影响。如果没有这些信息,MemberwiseClone() 会很棘手。 -1 没有提到 ICloneable 已被弃用,不应使用。 @Tom :-1 对您的评论(如果可以的话)未提供有关您的声明的任何链接,因为 ICloneable 在official doc 中未标记为已过时)。您更多的是谈论最佳实践,而不是官方弃用状态。 ... 但这会给出与用户原始问题相同的结果。直接来自文档:“如果字段是值类型,则执行字段的逐位复制。如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其克隆指的是同一个对象。” msdn.microsoft.com/en-us/library/… 嗯.. 它不被弃用,因此建议不要在公共 API 中使用,因为该 API 的消费者的实现不清楚,但对于内部使用或开源,我认为没关系。他们说的是“由于 Clone 的调用者不能依赖于执行可预测克隆操作的方法,我们建议不要在公共 API 中实现 ICloneable。”【参考方案4】:

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

类似这样的:

namespace Example

    class MyClass
    
        public int val;

        public MyClass()
        
        

        public MyClass(MyClass other)
        
            val = other.val;
        
    

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

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();
    

输出:

objectA.val = 10

objectB.val = 20               

【讨论】:

这对学习很有好处。不是为了练习。 输出的第二行应该是objectB.val = 20,无法编辑,因为编辑太小了 复制构造函数的问题是,如果添加/删除字段,还必须修改复制构造函数。这可能成为维护的噩梦。特别是对于具有许多字段的对象(50+,即 DataContract)。 我喜欢这个答案。 Clone 方法返回类对象,因此它是一种构造函数。那么,为什么叫它克隆而不使用类名呢?那么,何为“维修噩梦”?如果您需要维护大量字段,这是最好的选择。关于“弃用”:没有人强迫您从 IClonable 接口继承。您可以在没有接口的情况下实现 Clone() 方法。但毕竟我会在我的项目中实现复制构造函数。 我同意@Roland。使用 new 关键字,您肯定会创建一个具有不同地址的新对象。我看不出有什么问题。迷恋你可能更喜欢使用反射?

以上是关于在 C# 中创建对象的副本 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Python 中创建对象的副本?

如何在 PHP 中创建对象的副本?

有没有更好的方法在 C# 中创建深克隆和浅克隆?

如何在 Ruby 中创建对象的深层副本?

在 Rails 中创建对象及其所有关联模型的副本

javascript 如何在JavaScript中创建对象或数组的副本?