ppetrov
ppetrov

Reputation: 3105

Clone derived class from base class method

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

Upvotes: 14

Views: 25000

Answers (7)

Sriram Sakthivel
Sriram Sakthivel

Reputation: 73442

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(); 
    }
}

Upvotes: 16

mdonatas
mdonatas

Reputation: 1768

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; }
}

Upvotes: 0

Tomas Kubes
Tomas Kubes

Reputation: 25108

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;
            }
        }
    }
}

Upvotes: 0

Peter - Reinstate Monica
Peter - Reinstate Monica

Reputation: 16017

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

Upvotes: 3

Alexander Simonov
Alexander Simonov

Reputation: 1584

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;
    }
}

Upvotes: 20

Jeroen van Langen
Jeroen van Langen

Reputation: 22038

I got another idea using the Activator class:

public class Base
{
    public virtual object Clone()
    {
        Base copy = (Base)Activator.CreateInstance(this.GetType());
        copy.Id = this.Id;
        return copy;
    }


    public string Id { get; set; }
}

public class A : Base
{
    public override object Clone()
    {
        A copy = (A)base.Clone();
        copy.Name = this.Name;
        return copy;
    }

    public string Name { get; set; }
}

A a = new A();
A aCopy = (A)a.Clone();

But i would go for the Alexander Simonov answer.

Upvotes: 1

Jeroen van Langen
Jeroen van Langen

Reputation: 22038

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..

Upvotes: 3

Related Questions