首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >实现ICloneable的正确方法

实现ICloneable的正确方法
EN

Stack Overflow用户
提问于 2014-01-14 22:53:25
回答 7查看 102.4K关注 0票数 60

在类层次结构中实现ICloneable的正确方式是什么?假设我有一个抽象类DrawingObject。另一个抽象类RectangularObject继承自DrawingObject。然后还有多个具体的类,如ShapeTextCircle等,它们都继承自RectangularObject。我想在DrawingObject上实现ICloneable,然后在层次结构中向下携带它,在每一层复制可用的属性,并在下一层调用父代的Clone

但是问题是,由于前两个类是抽象的,所以我不能在Clone()方法中创建它们的对象。因此,我必须在每个具体的类中复制属性复制过程。还是有更好的方法?

EN

回答 7

Stack Overflow用户

发布于 2014-01-15 00:54:20

您可以使用object的受保护方法MemberwiseClone轻松创建一个表面上的克隆。

示例:

代码语言:javascript
复制
   public abstract class AbstractCloneable : ICloneable
   {
      public object Clone()
      {
         return this.MemberwiseClone();
      }
   }

如果你不需要像深度拷贝这样的东西,你就不需要在子类中做任何事情。

MemberwiseClone方法通过创建一个新对象,然后将当前对象的非静态字段复制到新对象来创建浅层副本。如果字段是值类型,则执行该字段的逐位复制。如果字段是引用类型,则复制引用,但不复制引用的对象;因此,原始对象及其克隆引用同一对象。

如果您在克隆逻辑中需要更多的智能,您可以添加一个虚拟方法来处理引用:

代码语言:javascript
复制
   public abstract class AbstractCloneable : ICloneable
   {
      public object Clone()
      {
         var clone = (AbstractCloneable) this.MemberwiseClone();
         HandleCloned(clone);
         return clone;
      }

      protected virtual void HandleCloned(AbstractCloneable clone)
      {
         //Nothing particular in the base class, but maybe useful for children.
         //Not abstract so children may not implement this if they don't need to.
      }
   }


   public class ConcreteCloneable : AbstractCloneable
   {
       protected override void HandleCloned(AbstractCloneable clone)
       {
           //Get whathever magic a base class could have implemented.
           base.HandleCloned(clone);

           //Clone is of the current type.
           ConcreteCloneable obj = (ConcreteCloneable) clone;

           //Here you have a superficial copy of "this". You can do whathever 
           //specific task you need to do.
           //e.g.:
           obj.SomeReferencedProperty = this.SomeReferencedProperty.Clone();
       }
   }
票数 81
EN

Stack Overflow用户

发布于 2014-01-14 23:05:16

为基类提供一个受保护的、可重写的CreateClone()方法,该方法创建当前类的新(空)实例。然后让基类的Clone()方法调用该方法,以多态方式实例化一个新实例,然后基类可以将其字段值复制到该实例中。

派生的非抽象类可以重写CreateClone()方法来实例化相应的类,并且所有引入新字段的派生类都可以重写Clone(),以便在调用继承版本的Clone()之后将其附加字段值复制到新实例中。

代码语言:javascript
复制
public CloneableBase : ICloneable
{
    protected abstract CloneableBase CreateClone();

    public virtual object Clone()
    {
        CloneableBase clone = CreateClone();
        clone.MyFirstProperty = this.MyFirstProperty;
        return clone;
    }

    public int MyFirstProperty { get; set; }
}

public class CloneableChild : CloneableBase
{
    protected override CloneableBase CreateClone()
    {
        return new CloneableChild();
    }

    public override object Clone()
    {
        CloneableChild clone = (CloneableChild)base.Clone();
        clone.MySecondProperty = this.MySecondProperty;
        return clone;
    }

    public int MySecondProperty { get; set; }
}

如果您想跳过第一个覆盖步骤(至少在默认情况下),您还可以假设一个默认的构造函数签名(例如,无参数),并尝试使用该构造函数签名和反射实例化一个克隆实例。这样,只有构造函数与默认签名不匹配的类才必须覆盖CreateClone()

默认CreateClone()实现的一个非常简单的版本可能如下所示:

代码语言:javascript
复制
protected virtual CloneableBase CreateClone()
{
    return (CloneableBase)Activator.CreateInstance(GetType());
}
票数 5
EN

Stack Overflow用户

发布于 2018-07-08 23:31:52

我相信我比@johnny5的优秀答案有了改进。只需在所有类和基类中定义复制构造函数,在Clone方法中使用反射来查找复制构造函数并执行它。我认为这稍微更干净一些,因为你不需要一堆处理克隆重写,也不需要MemberwiseClone(),因为在很多情况下,这只是一种过于生硬的工具。

代码语言:javascript
复制
public abstract class AbstractCloneable : ICloneable
    {
        public int BaseValue { get; set; }
        protected AbstractCloneable()
        {
            BaseValue = 1;
        }
        protected AbstractCloneable(AbstractCloneable d)
        {
            BaseValue = d.BaseValue;
        }

        public object Clone()
        {
            var clone = ObjectSupport.CloneFromCopyConstructor(this);
            if(clone == null)throw new ApplicationException("Hey Dude, you didn't define a copy constructor");
            return clone;
        }

    }


    public class ConcreteCloneable : AbstractCloneable
    {
        public int DerivedValue { get; set; }
        public ConcreteCloneable()
        {
            DerivedValue = 2;
        }

        public ConcreteCloneable(ConcreteCloneable d)
            : base(d)
        {
            DerivedValue = d.DerivedValue;
        }
    }

    public class ObjectSupport
    {
        public static object CloneFromCopyConstructor(System.Object d)
        {
            if (d != null)
            {
                Type t = d.GetType();
                foreach (ConstructorInfo ci in t.GetConstructors())
                {
                    ParameterInfo[] pi = ci.GetParameters();
                    if (pi.Length == 1 && pi[0].ParameterType == t)
                    {
                        return ci.Invoke(new object[] { d });
                    }
                }
            }

            return null;
        }
    }

最后,让我直言不讳地支持ICloneable。如果你使用这个接口,你会受到样式规则的打击,因为.NET框架设计指南说不要实现它,因为引用指南的话说,“当使用一个用ICloneable实现类型的对象时,你永远不知道你会得到什么。这使得这个接口变得毫无用处。”这意味着你不知道你得到的是深拷贝还是浅拷贝。这简直就是诡辩。这是否意味着永远不应该使用复制构造函数,因为“你永远不知道你会得到什么”?当然不是。如果你不知道你会得到什么,这只是类设计的问题,而不是接口的问题。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/21116554

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档