Clone derived class from base class method

2019-03-20 02:56发布

I have an abstract base class Base which has some common properties, and many derived ones which implement different logic but rarely have additional fields.

public abstract Base
{
    protected int field1;
    protected int field2;
    ....

    protected Base() { ... }
}

Sometimes I need to clone the derived class. So my guess was, just make a virtual Clone method in my base class and only override it in derived classes that have additional fields, but of course my Base class wouldn't be abstract anymore (which isn't a problem since it only has a protected constructor).

public Base
{
    protected int field1;
    protected int field2;
    ....

    protected Base() { ... }

    public virtual Base Clone() { return new Base(); }
}

public A : Base { }
public B : Base { }
  1. The thing is, since I can't know the type of the derived class in my Base one, wouldn't this lead to have a Base class instance even if I call it on the derived ones ? (a.Clone();) (actually after a test this is what is happening but perhaps my test wasn't well designed that's why I have a doubt about it)

  2. Is there a good way (pattern) to implement a base Clone method that would work as I expect it or do I have to write the same code in every derived class (I'd really like to avoid that...)

Thanks for your help

7条回答
Evening l夕情丶
2楼-- · 2019-03-20 03:45

I did something similar as Alexander Simonov, but perhaps simpler. The idea is (as I said in a comment) to have just one Clone() in the base class and leave all the work to a virtual CloneImpl() which each class defines as needed, relying on the CloneImpl()s of the base classes.

Creation of the proper type is left to C#'s MemberwiseClone() which will do whatever it takes for the object that's calling. This also obviates the need for a default constructor in any of the classes (none is ever called).

using System;

namespace CloneImplDemo
{
    // dummy data class
    class DeepDataT : ICloneable 
    {
        public int i;
        public object Clone() { return MemberwiseClone(); } 
    }

    class Base: ICloneable
    {
        protected virtual Base CloneImpl()
        { 
            // Neat: Creates the type of whatever object is calling. 
            // Also obviates the need for default constructors
            // (Neither Derived1T nor Derived2T have one.)
            return (Base)MemberwiseClone();
        }

        public object Clone() 
        {
            // Calls whatever CloneImpl the  
            // actual calling type implements.
            return CloneImpl();
        }
    }

    // Note: No Clone() re-implementation
    class Derived1T : Base
    {
        public Derived1T(int i) { der1Data.i = i; }
        public DeepDataT der1Data = new DeepDataT();
        protected override Base CloneImpl()
        {
            Derived1T cloned = (Derived1T)base.CloneImpl();
            cloned.der1Data = (DeepDataT)der1Data.Clone();
            return cloned;
        }
    }

    // Note: No Clone() re-implementation.
    class Derived2T : Derived1T
    {
        public Derived2T(int i1, int i2) : base(i1)
        {
            der2Data.i = i2;
        }
        public string txt = string.Empty; // copied by MemberwiseClone()
        public DeepDataT der2Data = new DeepDataT();
        protected override Base CloneImpl()
        {
            Derived2T cloned = (Derived2T)base.CloneImpl();
            // base members have been taken care of in the base impl.
            // we only add our own stuff.
            cloned.der2Data = (DeepDataT)der2Data.Clone();
            return cloned;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var obj1 = new Derived2T(1,2);
            obj1.txt = "this is obj1";

            var obj2 = (Derived2T)obj1.Clone();
            obj2.der1Data.i++;
            obj2.der2Data.i++; // changes value.
            obj2.txt = "this is a deep copy"; // replaces reference.

            // the values for i should differ because 
            // we performed a deep copy of the DeepDataT members.
            Console.WriteLine("obj1 txt, i1, i2: " + obj1.txt + ", " + obj1.der1Data.i + ", " + obj1.der2Data.i);
            Console.WriteLine("obj2 txt, i1, i2: " + obj2.txt + ", " + obj2.der1Data.i + ", " + obj2.der2Data.i);
        }
    }
}

Output:

obj1 txt, i1, i2: this is obj1, 1, 2
obj2 txt, i1, i2: this is a deep copy, 2, 3
查看更多
登录 后发表回答