正确实施ICloneable的方法

dot*_*NET 48 .net c# oop icloneable

ICloneable在类层次结构中实现的正确方法是什么?说我有一个抽象类DrawingObject.另一个抽象类RectangularObject继承自DrawingObject.随后有多个具体的类一样Shape,Text,Circle等所有的继承RectangularObject.我想要实现ICloneableDrawingObject,然后带着它到整个组织,在每个级别可复制性,并呼吁父母Clone在一个新的水平.

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

Joh*_*ny5 63

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

例:

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

如果您不需要任何类似深层复制的内容,则无需在子类中执行任何操作.

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

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

   public abstract class AbstractCloneable : ICloneable
   {
      public object Clone()
      {
         var clone = (AbstractCloneable) this.MemberwiseClone();
         HandleCloned(clone);
         return clone;
      }

      protected virtual 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 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();
       }
   }
Run Code Online (Sandbox Code Playgroud)

  • 当然,你总是可以调用`base.HandleCloned`来调用父类的逻辑。我认为这是涉及较少代码编写的解决方案。`MemberwiseClone` 处理对象创建的魔力。 (2认同)

O. *_*per 6

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

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

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; }
}
Run Code Online (Sandbox Code Playgroud)

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

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

protected virtual CloneableBase CreateClone()
{
    return (CloneableBase)Activator.CreateInstance(GetType());
}
Run Code Online (Sandbox Code Playgroud)


Gra*_*API 5

为了使用新引用创建深度克隆对象并避免在最意想不到的地方对对象进行突变,请使用序列化/反序列化。

它将允许完全控制可以克隆的内容(使用忽略属性)。这里有一些 System.Text.Json 和 Newtonsoft 的示例。

// System.Text.Json
public object Clone()
{
    // setup
    var json = JsonSerializer.Serialize(this);

    // get
    return JsonSerializer.Deserialize<MyType>(json);
}

// Newtonsoft
public object Clone()
{
    // setup
    var json = JsonConvert.SerializeObject(this);

    // get
    return JsonConvert.DeserializeObject<MyType>(json);
}

// Usage
MyType clonedMyType = myType.Clone();
Run Code Online (Sandbox Code Playgroud)


Ily*_*huk 0

BinaryFormatter在我看来,最清晰的方法是在 中应用二进制序列化MemoryStream

关于 C# 深度克隆的 MSDN 线程,其中建议使用上述方法。

  • 虽然这在某些情况下很有帮助,但缺点是您对克隆内容的控制可能较少。也许您不想克隆整个可到达的对象图,但实际上保持对某些(但不是全部)“中心对象”的引用完好无损。例如,绘图元素可能应该克隆其“子项目”,而不是其所有者文档。在实践中,我发现严格的浅克隆和严格的深克隆都不可取,通常是介于两者之间。 (3认同)