Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deep Copy/Clone methods in a C# class hierarchy - Do I need a concrete implementation everywhere?

Let's say I have the following class hierarchy (base interface included):

IAction -> (abstract) BaseAction -> (concrete) ImmediateAction -> (concrete) MovementAction

Now, let's say IAction exposes a method (well, really a different interface that IAction implements does but let's keep things simple here!):

// Returns a new IAction instance deep copied from the current instance.
IAction DeepClone();

Good so far? We have our deep copy method, and ImmediateAction has some properties that it wants copied so it will provide not only an implementation of DeepClone(), but a copy constructor as well:

//Base Action implementation
protected BaseAction(BaseAction old)
{
    this.something = old.something;
}

//Immediate Action Implementation
protected ImmediateAction(ImmediateAction old)
   : base(old)
{
    this.anything = old.anything;
}

public IAction DeepClone()
{
    return new ImmediateAction(this);
}

Now, let's say MovementAction doesn't have anything inside of it that's relevant in a DeepClone() at all, so it doesn't implement the method or a copy constructor.

The problem that I'm having is this:

IAction x = new MovementAction();
IAction y = x.DeepClone();

//pleaseBeTrue is false
bool pleaseBeTrue = y is MovementAction;

Now, I understand what's going on here - MovementAction does not implement DeepClone(), so ImmediateAction.DeepClone() is called instead, which instantiates a new ImmediateAction. Hence, the type of y in the above example is ImmediateAction instead of MovementAction.

So, after this lengthy preamble, my question is this: what is the best practice for this type of situation? Am I stuck? Do I simply have to implement a DeepClone() method no matter what for every class along the hierarchy? Is the pattern that I am using here incorrect, and there's a better way?

One final note: I would like to avoid reflection if at all possible.

like image 874
Terminal8 Avatar asked Oct 19 '12 00:10

Terminal8


People also ask

What is a deep copy in C?

A deep copy, in contrast, means that you copy an entire object (struct). If it has members that can be copied shallow or deep, you also make a deep copy of them.

What is deep copy with example?

It means first constructing a new collection object and then recursively populating it with copies of the child objects found in the original. In the case of deep copy, a copy of the object is copied into another object. It means that any changes made to a copy of the object do not reflect in the original object.


2 Answers

Could use an extension method and do incremental cloning

public static class DeepCopyExt
{
    public static T DeepCopy<T>(this T item)
        where T : ThingBase, new()
    {
        var newThing = new T();
        item.CopyInto(newThing);
        return newThing;
    }
}

public abstract class ThingBase
{
    public int A { get; set; }

    public virtual void CopyInto(ThingBase target)
    {
        target.A = A;
    }
}

public class ThingA : ThingBase
{
}

public class ThingB : ThingA
{
    public int B { get; set; }

    public override void CopyInto(ThingBase target)
    {
        var thingB = target as ThingB;

        if(thingB == null)
        {
           throw new ArgumentException("target is not a ThingB");
        }

        thingB.B = B;
        base.CopyInto(thingB);
    }
}

class Program
{
    static void Main(string[] args)
    {
        var b = new ThingB
        {
            A = 1,
            B = 3
        };

        //c is ThingB
        var c = b.DeepCopy();

        var b1 = new ThingA
        {
            A = 1,
        };

        //c1 is ThingA
        var c1 = b1.DeepCopy();

        Debugger.Break();
    }
}
like image 144
mlorbetske Avatar answered Sep 28 '22 18:09

mlorbetske


So yes, you have two options:

  • Either implement DeepClone() each time and in details (list all not-shared properties)
  • Or use 'quick&dirty' but shared implementation using reflection
like image 43
abatishchev Avatar answered Sep 28 '22 17:09

abatishchev