When working on some sort of data-driven project, I frequently have the need to allow deep-copying of data objects. There are several patterns that accomplish this, but I’ve settled on one in particular.
Most .NET developers are probably familiar with the ICloneable
interface. While this is a good starting point, it is not what I choose to rely on, for two reasons. First, the return type of the Clone method is object
, so a cast is required. Second, the interface doesn’t really give you any special functionality. Nonetheless, implementing interfaces is usually a good thing, so my approach does use the interface, if only as a tag.
(I am leaving out equality test implementations for the sake of brevity, but one usually wants to implement equality testing when an object can be cloned.)
The common pattern I see when implementing a Clone method is to declare one public virtual method on the base class:
abstract class Animal { public IList<Animal> Children { get; set; } public virtual Animal Clone() { var copy = (Animal)MemberwiseClone(); // Deep-copy children copy.Children = Children.Select(c => c.Clone()).ToList(); return copy; } }
Simple enough. Let’s say that we have a derived class that needs some additional logic. We just override the Clone method, right?
class Dog : Animal { public Collar Collar { get; set; } public virtual Animal Clone() { var copy = (Dog)base.Clone(); copy.Collar = Collar.Clone(); return copy; } }
This works, but as soon as you try to clone a Dog
directly, the ugliness of this pattern is apparent.
Animal animal = otherAnimal.Clone(); // Great! Dog dog = otherDog.Clone(); // Compile-time error
Unfortunately, the return type of Animal.Clone()
is Animal
and subclasses may not change the return type, not even to narrow it. So to clone a Dog
into a variable of type Dog
means we have to cast:
Dog dog = (Dog)otherDog.Clone();
Yuck. This is passable, but it’s hardly optimal.
The good news is that with just one tweak, we can make this pleasant to deal with. First, the Clone method needs to be made protected and renamed. Second, we create a new public Clone method that is not virtual and calls the protected virtual method. Subclasses hide this method with a new implementation that does the same thing, but casts the result.
Here’s the full implementation:
abstract class Animal : ICloneable { public IList<Animal> Children { get; set; } public Animal Clone() { return CloneImpl(); } object ICloneable.Clone() { return CloneImpl(); } protected virtual Animal CloneImpl() { var copy = (Animal)MemberwiseClone(); // Deep-copy children copy.Children = Children.Select(c => c.Clone()).ToList(); return copy; } } class Dog : Animal { public Collar Collar { get; set; } new public Dog Clone() { return (Dog)CloneImpl(); } protected virtual Animal CloneImpl() { var copy = (Dog)base.CloneImpl(); copy.Collar = Collar.Clone(); return copy; } }
Now, we have nice class-specific methods that will return a properly-typed reference to the new copy.
Animal animal = otherAnimal.Clone(); // Works Dog dog = otherDog.Clone(); // Also works!
It’s worth noting that both patterns will properly copy objects of more specific types than the reference you use to copy them. For example, given this variable:
Animal animal = new Dog();
animal.Clone()
will return a Dog
instance, typed as Animal
. This is what we’d expect.
Why not make an ICloneable interface that fixes the typing issue?
Wasn’t thinking about it – it took away the generic parts of this that made it make sense.
You could also just serialize the object to json and deserialize back using the class with json.net. Should be like 2 lines of code.
@Josh: Fixed the formatting for you. Your suggestion would apply to these classes, but would require a similar pattern as I have proposed to deal with the typing problem. (If you implement
ICloneable<Dog>
onDog
you could not expose itsClone()
method directly, as it would clash with the virtualClone()
defined on the superclass.) It’s a nice addition to my suggested pattern, but does not replace it.@Donny: You could, if you wanted to use a sledgehammer to kill a fly. I can’t imagine that using json.net to clone objects is going to be anywhere near as performant as
MemberwiseClone()
. (And having proper encapsulated deep-copying support is always a good thing. As the class author, you know what objects in your graph are mutable and need to be copied; json.net doesn’t, and will copy everything, whether or not it is necessary.)@Chris: I would say without doing any comparison speed tests. It looks fast enough.
http://james.newtonking.com/projects/json/help/?topic=html/Performance.htm
@Donny: I use JSON.NET myself, but for reading and writing JSON. 😉 Using it to copy objects seems like a misuse, and cannot possibly be regarded as efficient. Serialization is intended for data transmission and persistence. I’m sure it works, but there are lots of things that work that are regarded as bad practices.
@Chris: Now just like everything it depends on the context of the situation. If your doing gaming, any type of graphics or math based programming, then yes you should n’t use this method.
It would probably be a good idea to explicitly NOT implement IClonable, and also to call the method something else instead of Clone(). Using an interface implies that objects that implement the interface can be treated uniformly. The IClonable interface, however, doesn’t specify the behavior of the Clone() method (deep/versus shallow for instance). This means the interface doesn’t add any value, and widespread use will likely lead to flawed code later on (code that assumes ICloneables can be treated uniformly). Therefore, it’s better to avoid this interface completely.
and explicit ICloneable implementation, and Dog.Clone() would return instance of Dog
@Donny – wow man, you made my day 🙂 Yes, I have seen people use these bizarre methods to make a copy … but this is simply unacceptable in a framework/library. You might use it as a dirty hack when your manager keeps threatening you if you don’t finish your task in time.
@Denis – good idea, though extensions tend to “pollute” and also need to be explicitly added into scope.
I asked this as a question on StackOverflow sometime ago…. http://stackoverflow.com/questions/1573453/i-need-to-implement-c-sharp-deep-copy-constructors-with-inheritance-what-patter
@Anthony: A recent SO question is actually what prompted me to write this entry. 🙂
I’ve been doing something similar for some time:
And derived classes are implemented like:
I like having a common interface being implemented, because it gives me ability to write extension methods, for example:
@Alex Hacks are what the internet is built on. 😉
IMHO, I think the code posted by Denis is much more compact and has the same level of usability as yours and type safety. It will also work seamlessly with value types. Good one, Denis!
@Ananth: Assuming that you explicitly implement ICloneable, yes, it can be better. If ICloneable is not implemented or is implemented implicitly then this approach will not work.
Were I going to use this method I would probably create a new interface (Oskar raised some good points that I did not consider previously), and name the interface method and the extension method differently to ensure that the extension can always be easily used, whether or not the interface is implemented implicitly.
(OTOH, the extension may require a
using
statement in larger projects, while having a casting method in the class itself wouldn’t. It’s a trade-off.)Something that I didn’t state on my comment is that I never thought of using MemberwiseClone. It’s a good idea IF you remember to replace mutable object references with copies when doing a deep copy (shallow copying is a bad idea in a mutable environment).
Agreed. I like the idea of having copy constructors, but they might lead to object slicing if one isn’t careful. For example, if
B
derivesA
and both have copy constructors, you could pass aB
instance toA
‘s copy constructor and wind up with a copy of a different type than the original.MemberwiseClone()
ensures that you get an object of the exact same type, and each overload of theCloneImpl()
method (in my pattern) should call the base method and then do whatever deep-copying is necessary for that specific class. The result is the ability to clone an object through a reference of typeT
, obtain a result typed asT
, where the result will have the same actual type as the original object (T
, or something more specific thanT
).