【问题标题】:How to Implement Clone and Copy method inside a Class?如何在类中实现克隆和复制方法?
【发布时间】:2010-11-11 08:59:10
【问题描述】:

我有一个名为 Employee 的类,其中有 3 个属性名为 ID,Name,Dept。我需要实现Copy 和Clone 方法吗?当我使用Copy 或Clone 方法时,我需要避免强制转换吗?我该怎么做呢?

示例:与DataTable 相同,具有DataTable.Copy() 和DataTable.Clone()。

【问题讨论】:

    标签: c# casting copy clone


    【解决方案1】:

    你需要实现IClonable接口并提供clone方法的实现。如果你想避免强制转换,不要实现这个。

    一个简单的深度克隆方法可能是将对象序列化到内存然后反序列化它。您的类中使用的所有自定义数据类型都需要使用 [Serializable] 属性进行序列化。对于克隆,您可以使用类似

      public MyClass Clone()
        {
            MemoryStream ms = new MemoryStream();
            BinaryFormatter bf = new BinaryFormatter();
    
            bf.Serialize(ms, this);
    
            ms.Position = 0;
            object obj = bf.Deserialize(ms);
            ms.Close();
    
            return obj as MyClass;
        }
    

    如果您的类只有value types,那么您可以使用copy constructor 或只是将值分配给 Clone 方法中的新对象。

    【讨论】:

    • @Rowland - 复制构造函数可能不会深度复制内容,并且在添加字段时需要维护。二进制序列化是一种非常常见的深拷贝方式。
    • 技术上 C# 没有复制构造函数。你可以编写一个构造函数,接受相同类型的参数并手动复制属性,但是如果你想做一个深拷贝,可能会有点麻烦。
    • 进行序列化+反序列化进行克隆似乎很浪费(额外的内存和 CPU 使用),但它确实具有简单的吸引力 - 与反映整个对象图或添加克隆支持所有沿着图表向下(如果你不控制每个子类,这是不可能的)。
    • @我是否同意需要在对象树中的所有类上实现复制构造函数;但同样,您必须检查每个类是否也是可序列化的。
    • @Rowland - 如果不是,至少你会得到一个例外!
    【解决方案2】:

    您是否必须使用 ICloneable 接口,或者仅在通用接口中定义两个名为 Clone 和 Copy 的方法就足够了?

    public class YourClass : ICloneable<YourClass>
    {
        // Constructor logic should be here
        public YourClass Copy() { return this; }        
        public YourClass Clone() { return new YourClass(ID, Name, Dept); }
    }
    
    interface  IClonable<T>
    {
        T Copy();
        T Clone();
    }
    

    还是我误会了什么?

    我想说的是,您不必让它变得更复杂吗?如果您需要您的对象符合某种要求,如果 .Net 框架中指定的对象对于这种情况来说过于复杂,您可以自己编写它。您还应该定义与Clone 和Copy 的区别,即它们对您意味着什么?我知道有几个站点指定Clone 是深拷贝,Copy 是浅拷贝。

    【讨论】:

    • 唯一的问题是维护开销。如果添加新字段,则需要更新 CopyClone() 方法以合并它(以及它调用的构造函数)。如果要调用构造函数,而不是传递离散字段,则应传递整个对象,即:return new YourClass(this)
    • @Michael,这当然是真的。但关键是在你真正需要它之前不要让它变得更复杂。并且 Copy 方法可能是错误的(将更改它:))。您可能可以将我拥有的 IClonable 接口更改为一个抽象类,该类包含一个使用反射或 Midhat 在下面编写的序列化模式的 Clone 方法。
    • 甚至我对来自 diff 的文档感到困惑。网站?。
    • 不要混淆,而是要弄清楚你的需求。为什么要复制/克隆?您的所有对象都需要它吗?你想要复制/克隆的特殊逻辑吗?回答这些问题,然后实施适合您需求的解决方案。您可以做的一个很好的功能是使 IClonable 抽象,然后使用使用反射的通用克隆方法。该克隆方法可以为要排除或包含的属性使用 params 参数,请参阅此处的 MapRow 方法:blog.tomasjansson.com/2010/10/generic-mapper-for-datareader 以了解包含/排除如何工作的示例。
    • Thomas 的原始帖子指出 “我知道有几个网站指定 Clone 是深拷贝”。我认为这是一个危险的假设。例如,MSDN 声明 “Clone 的实现可以执行深拷贝或浅拷贝。” SOURCE:ICloneable.Clone Method
    【解决方案3】:

    你的意思是,如何实现 ICloneable.Clone() 并让它返回类本身的类型。

    public class MyType : ICloneable
    {
      public MyType Clone() //called directly on MyType, returns MyType
      {
        return new MyType(/* class-dependant stuff goes here */);
      }
      object ICloneable.Clone() // called through ICloneable interface, returns object
      {
        return Clone();
      }
    }
    

    【讨论】:

      【解决方案4】:

      【讨论】:

      • 也只做浅拷贝,如果你需要深拷贝,你必须编辑 IL 生成器以递归地浏览图表。
      • 有一个更新的版本可用:whizzodev.blogspot.com/2008/06/…
      【解决方案5】:

      我经常看到建议将复制构造函数作为克隆方法的替代方法,但除了密封类之外,其行为非常不同。如果我有一个类型 Car,它只支持属性 VIN、BodyColor 和 BodyStyle,以及一个衍生类型 FancyCar,它也支持 InteriorFabric 和 SoundSystem,那么接受 Car 类型对象并使用 Car 复制构造函数复制它的代码将结束开一辆车。如果将 FancyCar 传递给此类代码,则生成的“副本”将是一辆新车,其 VIN、BodyColor 和 BodyStyle 与原车匹配,但没有任何 InteriorFabric 或 SoundSystem。相比之下,代码接受 Car 并在其上使用克隆方法,将 FancyCar 传递给代码将导致 FancyCar 生成。

      除非想要使用反射,否则任何克隆方法都必须在其基础上调用 base.MemberwiseClone。由于 MemberwiseClone 不是虚拟方法,我建议定义一个受保护的虚拟克隆方法;您可能还希望通过定义具有相同名称的受保护范围的虚拟嵌套类来防止任何子类调用 MemberwiseClone(因此,如果后代类尝试调用 base.MemberwiseClone,它不会被解释为对虚拟类)。

      【讨论】:

        【解决方案6】:

        这里是一个例子:

        namespace XXX
        {
             [Serializable]
            public class ItemChecklist : ICloneable
            {
        
               // [...here properties, attributes, etc....]
        
        
        
                object ICloneable.Clone()
                {
                    return this.Clone();
                }
                public ItemChecklist Clone()
                {
                    return (ItemChecklist)this.MemberwiseClone();
                }
        
        
            }
        }
        

        即如果您使用此函数,您将在“itemAdd”中拥有对象“itemTemp”及其所有值的完整副本。

        ItemChecklist itemAdd = itemTemp.Clone();
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-12-22
          • 2019-11-27
          • 2023-03-12
          • 2018-09-20
          • 2018-04-28
          • 2014-05-23
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多