Clone derived class from base class method

后端 未结 7 2227
萌比男神i
萌比男神i 2021-02-08 17:09

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.

<
相关标签:
7条回答
  • 2021-02-08 17:46

    You can add a copy constructor to your base class:

    public abstract Base
    {
        protected int field1;
        protected int field2;
    
        protected Base() { ... }
    
        protected Base(Base copyThis) : this()
        { 
            this.field1 = copyThis.field1;
            this.field2 = copyThis.field2;
        }
    
        public abstract Base Clone();
    }
    
    public Child1 : Base
    {
        protected int field3;
    
        public Child1 () : base() { ... }
    
        protected Child1 (Child1  copyThis) : base(copyThis)
        {
            this.field3 = copyThis.field3;
        }
    
        public override Base Clone() { return new Child1(this); }
    }
    
    public Child2 : Base
    {
        public Child2 () : base() { ... }
    
        protected Child (Child  copyThis) : base(copyThis)
        {  }
    
        public override Base Clone() { return new Child2(this); }
    }
    
    public Child3 : Base
    {
        protected int field4;
    
        public Child3 () : base() { ... }
    
        protected Child3 (Child3  copyThis) : base(copyThis)
        {
            this.field4 = copyThis.field4;
        }
    
        public override Base Clone()
        {
            var result = new Child1(this);
            result.field1 = result.field2 - result.field1;
        }
    }
    
    0 讨论(0)
  • 2021-02-08 17:46

    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
    
    0 讨论(0)
  • 2021-02-08 17:49

    You could do something like this:

    public class Base<T> where T: Base<T>, new()
    {
        public virtual T Clone() 
        { 
            T copy = new T();
            copy.Id = this.Id;
            return copy;
        }
    
        public string Id { get; set; }
    }
    
    public class A : Base<A>
    {
        public override A Clone()
        {
            A copy = base.Clone();
            copy.Name = this.Name;
            return copy;
        }
    
        public string Name { get; set; }
    }
    
    private void Test()
    {
        A a = new A();
        A aCopy = a.Clone();
    }
    

    But i doubt that it will bring something useful. I'll create another example..

    0 讨论(0)
  • 2021-02-08 17:58

    If performance is not important for your case, you can simplify your code by creating just one general clone method which can clone whatever to whatever if properties are same:

    Base base = new Base(){...};
    Derived derived = XmlClone.CloneToDerived<Base, Derived>(base);
    
    
    public static class XmlClone
    {
        public static D CloneToDerived<T, D>(T pattern)
            where T : class
        {
            using (var ms = new MemoryStream())
            {
                using (XmlWriter writer = XmlWriter.Create(ms))
                {
                    Type typePattern = typeof(T);
                    Type typeTarget = typeof(D);
    
                    XmlSerializer xmlSerializerIn = new XmlSerializer(typePattern);
                    xmlSerializerIn.Serialize(writer, pattern);
                    ms.Position = 0;
                    XmlSerializer xmlSerializerOut = new XmlSerializer(typeTarget, new XmlRootAttribute(typePattern.Name));
                    D copy = (D)xmlSerializerOut.Deserialize(ms);                    
                    return copy;
                }
            }
        }
    }
    
    0 讨论(0)
  • 2021-02-08 18:07

    Just override the Clone and have another method to CreateInstance then do your stuff.

    This way you could have only Base class avoiding generics.

    public Base
    {
        protected int field1;
        protected int field2;
        ....
    
        protected Base() { ... }
    
        public virtual Base Clone() 
        { 
            var bc = CreateInstanceForClone();
            bc.field1 = 1;
            bc.field2 = 2;
            return bc;
        }
    
        protected virtual Base CreateInstanceForClone()
        {
            return new Base(); 
        }
    }
    
    
    public A : Base 
    {     
        protected int fieldInA;
        public override Base Clone() 
        { 
            var a = (A)base.Clone();
            a.fieldInA =5;
            return a;
        }
    
        protected override Base CreateInstanceForClone()
        {
            return new A(); 
        }
    }
    
    0 讨论(0)
  • 2021-02-08 18:11

    Found this question while trying to solve this exact problem, had some fun with LINQPad while at it. Proof of concept:

    void Main()
    {
        Person p = new Person() { Name = "Person Name", Dates = new List<System.DateTime>() { DateTime.Now } };
    
        new Manager()
        {
            Subordinates = 5
        }.Apply(p).Dump();
    }
    
    public static class Ext
    {
        public static TResult Apply<TResult, TSource>(this TResult result, TSource source) where TResult: TSource
        {
            var props = typeof(TSource).GetProperties(BindingFlags.Public | BindingFlags.Instance);
            foreach (var p in props)
            {
                p.SetValue(result, p.GetValue(source));
            }
    
            return result;
        }
    }
    
    class Person 
    {
        public string Name { get; set; }
        public List<DateTime> Dates { get; set; }
    }
    
    class Manager : Person
    {
        public int Subordinates { get; set; }
    }
    
    0 讨论(0)
提交回复
热议问题