Copy constructor versus Clone()
Asked Answered
c#
E

12

129

In C#, what is the preferred way to add (deep) copy functionality to a class? Should one implement the copy constructor, or rather derive from ICloneable and implement the Clone() method?

Remark: I wrote "deep" within brackets because I thought it was irrelevant. Apparently others disagree, so I asked whether a copy constructor/operator/function needs to make clear which copy variant it implements.

Ehrman answered 27/7, 2010 at 15:45 Comment(0)
I
101

You should not derive from ICloneable.

The reason is that when Microsoft designed the .net framework they never specified whether the Clone() method on ICloneable should be a deep or shallow clone, thus the interface is semantically broken as your callers won't know whether the call will deep or shallow clone the object.

Instead, you should define your own IDeepCloneable (and IShallowCloneable) interfaces with DeepClone() (and ShallowClone()) methods.

You can define two interfaces, one with a generic parameter to support strongly typed cloning and one without to keep the weakly typed cloning ability for when you are working with collections of different types of cloneable objects:

public interface IDeepCloneable
{
    object DeepClone();
}
public interface IDeepCloneable<T> : IDeepCloneable
{
    T DeepClone();
}

Which you would then implement like this:

public class SampleClass : IDeepCloneable<SampleClass>
{
    public SampleClass DeepClone()
    {
        // Deep clone your object
        return ...;
    }
    object IDeepCloneable.DeepClone()   
    {
        return this.DeepClone();
    }
}

Generally I prefer to use the interfaces described as opposed to a copy constructor it keeps the intent very clear. A copy constructor would probably be assumed to be a deep clone, but it's certainly not as much of a clear intent as using an IDeepClonable interface.

This is discussed in the .net Framework Design Guidelines and on Brad Abrams' blog

(I suppose if you are writing an application (as opposed to a framework/library) so you can be sure no one outside of your team will be calling your code, it doesn't matter so much and you can assign a semantic meaning of "deepclone" to the .net ICloneable interface, but you should make sure this is well documented and well understood within your team. Personally I'd stick to the framework guidelines.)

Infallible answered 27/7, 2010 at 16:0 Comment(9)
If you're going for an interface, how about having DeepClone(of T)() and DeepClone(of T)(dummy as T), both of which return T? The latter syntax would allow T to be inferred based upon the argument.Grigson
@supercat: Are you saying have a dummy parameter so the type can be inferred? It's an option I suppose. I'm not sure I like having a dummy parameter just to get the type inferred automatically. Perhaps I'm misunderstanding you. (Maybe post some code in a new answer so I can see what you mean).Infallible
@supercat: The dummy parameter would exist precisely to allow type inference. There are situations where some code might want to clone something without having ready access to what the type is (e.g. because it's a field, property, or function return from another class) and a dummy parameter would allow a means of properly inferring the type. Thinking about it, it's probably not really helpful since the whole point of an interface would be to create something like a deep-cloneable collection, in which case the type should be the collections' generic type.Grigson
@abatishchev: There are two interfaces. The explicit interface implementation is for the non-generic DeepClone method that returns just an object. The implicit one is for the generic interface so can be used when the exact type is known.Infallible
Question! In what situation would you ever want the non generic version? To me, it makes sense for only IDeepCloneable<T> to exist, because ... you do know what T if you make your own implementation, ie SomeClass : IDeepCloneable<SomeClass> { ... }Norman
@Kyle say you had a method that took cloneable objects, MyFunc(IDeepClonable data), then it could work on all clonables, not just a specific type. Or if you had a collection of cloneables. IEnumerable<IDeepClonable> lotsOfCloneables then you could clone lots of objects at the same time. If you don't need that kind of thing though, then leave the non-generic one out.Infallible
And what about method parameters? How they are copied, if I didn't implemented any interface?Domitian
I don't think a cloneable interface that is generic or uses object makes sense. I just can't see too many uses where you would need this to be an interface instead of just adding it directly to the type you are wanting to copy.Corybantic
Great answer to extend long-ago posts about implementing ICloneable. But with T DeepClone(); I get this warning 'IDeepCloneable<T>.DeepClone()' hides inherited member 'IDeepCloneable.DeepClone()'. Use the new keyword if hiding was intended. I think it needs to be new T DeepClone(); as in https://mcmap.net/q/175558/-is-there-ever-a-reason-to-hide-inherited-members-in-an-interface and https://mcmap.net/q/175559/-generic-interface-inheriting-non-generic-one-cTampere
B
33

In C#, what is the preferred way to add (deep) copy functionality to a class? Should one implement the copy constructor, or rather derive from ICloneable and implement the Clone() method?

The problem with ICloneable is, as others have mentioned, that it does not specify whether it is a deep or shallow copy, which makes it practically unuseable and, in practice, rarely used. It also returns object, which is a pain, since it requires a lot of casting. (And though you specifically mentioned classes in the question, implementing ICloneable on a struct requires boxing.)

A copy constuctor also suffers from one of the problems with ICloneable. It isn't obvious whether a copy constructor is doing a deep or shallow copy.

Account clonedAccount = new Account(currentAccount); // Deep or shallow?

It would be best to create a DeepClone() method. This way the intent is perfectly clear.

This raises the question of whether it should be a static or instance method.

Account clonedAccount = currentAccount.DeepClone();  // instance method

or

Account clonedAccount = Account.DeepClone(currentAccount); // static method

I slightly prefer the static version sometimes, just because cloning seems like something that is being done to an object rather than something the object is doing. In either case, there are going to be issues to deal with when cloning objects that are part of an inheritence hierarchy, and how those issues are delt with may ultimately drive the design.

class CheckingAccount : Account
{
    CheckAuthorizationScheme checkAuthorizationScheme;

    public override Account DeepClone()
    {
        CheckingAccount clone = new CheckingAccount();
        DeepCloneFields(clone);
        return clone;
    }

    protected override void DeepCloneFields(Account clone)
    {
        base.DeepCloneFields(clone);

        ((CheckingAccount)clone).checkAuthorizationScheme = this.checkAuthorizationScheme.DeepClone();
    }
}
Barkentine answered 27/7, 2010 at 16:15 Comment(4)
Although I don't know if the DeepClone() option is best, I like your answer a lot, as it underlines the confusing situation that exists about an in my opinion basic programming language feature. I guess it's up to the user to chose which option he likes best.Ehrman
I am not going to argue the points here, but in my opinion, the caller should not care so much about deep or shallow when they call Clone(). They should know that they're getting a clone with no invalid shared-state. For instance, it's perfectly possible that in a deep clone, I may not want to deep clone every element. All that the caller of Clone should care about is that they are getting a new copy that doesn't have any invalid and unsupported references to the original. Calling the method 'DeepClone" seems to convey too much implementation detail to the caller.Millikan
What's wrong with an object instance knowing how to clone itself instead of being copied by a static method? This occurs in the real world all the time with biological cells. Cells in your own body are busy cloning themselves right now as you read this. IMO, the static method option is more cumbersome, tends to hide the functionality, and deviates from using the "least surprising" implementation for the benefit of others.Aeronautics
@KenBeckett - The reason I feel that cloning is something done to an object is because an object should "do one thing and do it well." Normally, making copies of itself is not the core competency of a class, but rather that is functionality that is tacked on. Making a clone of a BankAccount is something that you very well might want to do, but making clones of itself is not a feature of a bank account. Your cell example is not broadly instructive, because reproduction is precisely what cells evolved to do. Cell.Clone would be a good instance method, but this is not true for most other things.Barkentine
G
29

I recommend using a copy constructor over a clone method primarily because a clone method will prevent you from making fields readonly that could have been if you had used a constructor instead.

If you require polymorphic cloning, you can then add an abstract or virtual Clone() method to your base class that you implement with a call to the copy constructor.

If you require more than one kind of copy (ex: deep/shallow) you can specify it with a parameter in the copy constructor, although in my experience I find that usually a mixture of deep and shallow copying is what I need.

Ex:

public class BaseType {
   readonly int mBaseField;

   public BaseType(BaseType pSource) =>
      mBaseField = pSource.mBaseField;

   public virtual BaseType Clone() =>
      new BaseType(this);
}

public class SubType : BaseType {
   readonly int mSubField;

   public SubType(SubType pSource)
   : base(pSource) =>
      mSubField = pSource.mSubField;

   public override BaseType Clone() =>
      new SubType(this);
}
Gestate answered 9/2, 2012 at 5:14 Comment(1)
+1 For addressing polymorphic cloning; a significant application of cloning.Selwin
A
19

There is a great argument that you should implement clone() using a protected copy constructor

It is better to provide a protected (non-public) copy constructor and invoke that from the clone method. This gives us the ability to delegate the task of creating an object to an instance of a class itself, thus providing extensibility and also, safely creating the objects using the protected copy constructor.

So this is not a "versus" question. You may need both copy constructor(s) and a clone interface to do it right.

(Although the recommended public interface is the Clone() interface rather than Constructor-based.)

Don't get caught-up in the explicit deep or shallow argument in the other answers. In the real world it is almost always something in-between - and either way, should not be the caller's concern.

The Clone() contract is simply "won't change when I change the first one". How much of the graph you have to copy, or how you avoid infinite recursion to make that happen shouldn't concern the caller.

Ailing answered 17/9, 2013 at 20:9 Comment(4)
"should not be the caller's concern". I could not agree more but, here I am, trying to figure out if List<T> aList=new List<T>(aFullListOfT) will do a deep copy(which is what I want) or a shallow Copy(which would break my code) and whether I have to implement another way to get the job done!Offer
A list<T> is too generic (ha ha) for clone to even make sense. In your case, that is certainly only a copy of the list and NOT the objects pointed to by the list. Manipulating the new list won't affect the first list, but the objects are the same and unless they are immutable, they ones in the first set will change if you change the ones in the second set. If there were a list.Clone() operation in your library, you should expect the result to be a full clone as in "won't change when I do something to the first one." that applies to the contained objects as well.Ailing
A List<T> isn't going to know anything more about properly cloning it's contents than you are. If the underlying object is immutable, you are good to go. Otherwise, if the underlying object has a Clone() method you will have to use it. List<T> aList = new List<T>(aFullListOfT.Select(t=t.Clone())Ailing
+1 for the hybrid approach. Both approaches have an advantage and disadvantage, but this seems to have the more overall benefit.Norman
C
12

Implementing ICloneable's not recommended due to the fact that it's not specified whether it's a deep or shallow copy, so I'd go for the constructor, or just implement something yourself. Maybe call it DeepCopy() to make it really obvious!

Cauline answered 27/7, 2010 at 15:50 Comment(8)
@Grant, how does the constructor relay intent? IOW, if an object took itself in the constructor, is the copy deep or shallow? Otherwise, I agree completely with the DeepCopy() (or otherwise) suggestion.Locomotive
I'd argue that a constructor is almost as unclear as the ICloneable interface - you'd have to read the API docs/code to know it does a deep clone or not. I just define an IDeepCloneable<T> interface with a DeepClone() method.Celestyna
@Jon - The reactoring's never finished!Cauline
@Marc, @Kent - yeah fair point, the constructor probably isn't a good idea either.Cauline
Bugger, I've come across some of our code that implements ICloneable :-(Zettazeugma
Has anyone seen a use where iCloneable was used on an object of unknown type? The whole point of interfaces is that they can be used on objects of unknown type; otherwise one may as well simply make Clone be a standard method which returns the type in question.Grigson
@JLWarlow - but does it do a shallow copy or a deep copy?! :-)Cauline
@Grant Crofton - exactly :-) I'll have to examine the implementation code to find out :-(Zettazeugma
I
12

You'll run into problems with copy constructors and abstract classes. Imagine you want to do the following:

abstract class A
{
    public A()
    {
    }

    public A(A ToCopy)
    {
        X = ToCopy.X;
    }
    public int X;
}

class B : A
{
    public B()
    {
    }

    public B(B ToCopy) : base(ToCopy)
    {
        Y = ToCopy.Y;
    }
    public int Y;
}

class C : A
{
    public C()
    {
    }

    public C(C ToCopy)
        : base(ToCopy)
    {
        Z = ToCopy.Z;
    }
    public int Z;
}

class Program
{
    static void Main(string[] args)
    {
        List<A> list = new List<A>();

        B b = new B();
        b.X = 1;
        b.Y = 2;
        list.Add(b);

        C c = new C();
        c.X = 3;
        c.Z = 4;
        list.Add(c);

        List<A> cloneList = new List<A>();

        //Won't work
        //foreach (A a in list)
        //    cloneList.Add(new A(a)); //Not this time batman!

        //Works, but is nasty for anything less contrived than this example.
        foreach (A a in list)
        {
            if(a is B)
                cloneList.Add(new B((B)a));
            if (a is C)
                cloneList.Add(new C((C)a));
        }
    }
}

Right after doing the above, you start wishing you'd either used an interface, or settled for a DeepCopy()/ICloneable.Clone() implementation.

Instructor answered 26/8, 2010 at 8:7 Comment(1)
Good argument for an Interface-based approach.Ailing
L
4

The problem with ICloneable is both intent and consistency. It's never clear whether it is a deep or shallow copy. Because of that, it's probably never used in only one manner or another.

I don't find a public copy constructor to be any clearer on that matter.

That said, I would introduce a method system that works for you and relays intent (a'la somewhat self documenting)

Locomotive answered 27/7, 2010 at 15:51 Comment(0)
F
4

If the object you are trying to copy is Serializable you can clone it by serializing it and deserializing it. Then you don't need to write a copy constructor for each class.

I don't have access to the code right now but it is something like this

public object DeepCopy(object source)
{
   // Copy with Binary Serialization if the object supports it
   // If not try copying with XML Serialization
   // If not try copying with Data contract Serailizer, etc
}
Foulk answered 27/7, 2010 at 15:54 Comment(3)
The use of serialization as a means of implementing deep cloning is irrelevant to the question of whether the deep clone should be surfaced as a ctor or method.Celestyna
I think it is another valid alternative. I didn't think he was limited to those two methods of deep copying.Foulk
@Kent Boogaart - Given the OP starts with the line "In C#, what is the preferred way to add (deep) copy functionality to a class", I think it is fair enough for Shaun to offer different alternatives. Especially in a legacy scenario where you have a large number of classes that you want to implement clone functionality for, this trick can be useful; not as lightweight as implementing your own clone directly, but nevertheless useful. If people never offered "have you thought of..." alternatives to my questions, I'd not have learned nearly as much as I have over the years.Battleax
I
1

It is dependent on copy semantics of the class in question, which you should define yourself as the developer. Chosen method is usually based on intended use cases of the class. Maybe it will make a sense to implement both methods. But both share similar disadvantage - it is not exactly clear which copying method they implement. This should be clearly stated in documentation for your class.

For me having:

// myobj is some transparent proxy object
var state = new ObjectState(myobj.State);

// do something

myobject = GetInstance();
var newState = new ObjectState(myobject.State);

if (!newState.Equals(state))
    throw new Exception();

instead of:

// myobj is some transparent proxy object
var state = myobj.State.Clone();

// do something

myobject = GetInstance();
var newState = myobject.State.Clone();

if (!newState.Equals(state))
    throw new Exception();

looked as clearer statement of intent.

Investigation answered 27/7, 2010 at 17:30 Comment(0)
P
0

If you read through all the interesting answers and discussions, you might still ask yourself how exactly you copy the properties - all of them explicitly, or is there a more elegant way to do it? If that is your remaining question, take a look at this (at StackOverflow):

How can I “deeply” clone the properties of 3rd party classes using a generic extension method?

It describes how to implement an extension method CreateCopy() which creates a "deep" copy of the object including all properties (without having to copy property by property manually).

Penny answered 27/7, 2010 at 15:45 Comment(0)
G
0

I think there should be a standard pattern for cloneable objects, though I'm not sure what exactly the pattern should be. With regard to cloning, it would seem there are three types of classes:

  1. Those that explicitly support for deep cloning
  2. Those that where memberwise cloning will work as deep cloning, but which neither have nor need explicit support.
  3. Those which cannot be usefully deep cloned, and where memberwise cloning will yield bad results.

So far as I can tell, the only way (at least in .net 2.0) to get a new object of the same class as an existing object is to use MemberwiseClone. A nice pattern would seem to be to have a "new"/"Shadows" function Clone which always returns the present type, whose definition is always to call MemberwiseClone and then call a protected virtual subroutine CleanupClone(originalObject). The CleanupCode routine should call base.Cleanupcode to handle the base type's cloning needs and then add its own cleanup. If the cloning routine has to use the original object, it would have to be typecast, but otherwise the only typecasting would be on the MemberwiseClone call.

Unfortunately, the lowest level of class that was of type (1) above rather than type (2) would have to be coded to assume that its lower types would not need any explicit support for cloning. I don't really see any way around that.

Still, I think having a defined pattern would be better than nothing.

Incidentally, if one knows that one's base type supports iCloneable, but does not know the name of the function it uses, is there any way to reference the iCloneable.Clone function of one's base type?

Grigson answered 28/7, 2010 at 15:20 Comment(0)
C
0

It's very simple to do this with NewtonSoft.Json

// get a MyClass object 'myObject' to create a clone
public class MyClass {
    public MyClass Copy()
        => Newtonsoft.Json.JsonConvert.DeserializeObject<MyClass >(Newtonsoft.Json.JsonConvert.SerializeObject(this)) ?? new();
}

MyClass myObject = new(MyClass);
MyClass myCopy = myObject.Copy();
Crispy answered 23/11, 2023 at 5:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.