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条回答
欢心
2楼-- · 2019-03-20 03:19

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; }
}
查看更多
祖国的老花朵
3楼-- · 2019-03-20 03:20

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.

查看更多
姐就是有狂的资本
4楼-- · 2019-03-20 03:24

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;
    }
}
查看更多
地球回转人心会变
5楼-- · 2019-03-20 03:28

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;
            }
        }
    }
}
查看更多
孤傲高冷的网名
6楼-- · 2019-03-20 03:34

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(); 
    }
}
查看更多
姐就是有狂的资本
7楼-- · 2019-03-20 03:35

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

查看更多
登录 后发表回答