Object cloning are one of the patterns we find in most software projects. It seems simple, so we easily run into a trivial implementation. But as code gets more complex, the cloning implementation rapidly gets larger and larger — and ugly. To keep cloning simple, I forced myself to follow a simple design pattern. Here is it:
1: class Cloneable : ICloneable
2: {
3: public object Clone()
4: {
5: Cloneable clone = this.MemberwiseClone();
6: this.CopyTo( clone );
7: return clone;
8: }
9:
10: protected virtual void CopyTo(Cloneable clone)
11: {
12: // Children should implement this method to
13: // clone cloneable properties.
14: }
15: }
The advantage of using MemberwiseClone is that I easily create a new instance. Additionally, it is really fast (the Rotor implementation uses memcpy). No need of reflection. It actually makes a shallow copy, so a big part of the job is already done:
- We have a shallow copy of fields of value types.
- Immutable reference types (like string) do not need to be cloned.
The goal of the CopyTo method is to take care of fields requiring special attention. The design pattern says that every class should call base.CopyTo() and make a deep copy of the fields it defines, for instance:
1: public class Address : Cloneable
2: {
3: // Immutables.
4: public string AddressLine1, AddressLine2, AddressLine3, Zip, City;
5: // Value type.
6: public int CountryID;
7:
8: // No CopyTo: a shallow copy is sufficient.
9:
10: }
11:
12: public class Party : Cloneable
13: {
14: public int ID; // Value-type.
15: public string FirstName, LastName; // Immutables.
16: public Address LegalAddress; // Cloneable.
17:
18: protected override void CopyTo(Cloneable clone)
19: {
20: base.CopyTo(clone);
21: ((Party) clone).LegalAddress = (Address) this.LegalAddress.Clone();
22: }
23: }
24:
25: public class Customer : Party
26: {
27: public Address DeliveryAddress;
28:
29: protected override void CopyTo(Cloneable clone)
30: {
31: base.CopyTo(clone);
32: ((Customer) clone).DeliveryAddress = (Address) this.DeliveryAddress.Clone();
33: }
34:
35: }
This may looks trivial, and indeed it is, but it works so well!
Implementing CopyTo() Automatically
You could expect me on that point. CopyTo is basically plumbing code no one wants to write. There should be a way to implement it automatically.
Can you use PostSharp for this? Of course! The PostSharp implementation of this pattern is actually a part of the sample
PostSharp.Samples.Librarian, a sample released to the public domain (and packaged with PostSharp).
It generates code to clone cloneable fields, and raise a build-time error when it does not know how to cope with that field type. I won’t comment further on that until you ask; please refer to the sample.
However, I don’t use PostSharp for this pattern. The reason is that the PostSharp-based solution is implemented using the low-level APIes and I don’t want to use them unless it is really necessary. And since I don’t have any special requirement (like performance or CAS), I use… System.Reflection.
There is an old adage telling that when someone has a hammer, everything looks like a nail. I try not to fall in that trap! The implementation simply iterate over all fields (or properties) of the type, reads them from the source object and assigns the value to the clone.
Cloning Trees
What is a little more difficult is to clone trees when children have a relationship to the parent. I have defined an interface:
1: public interface ICloneableTreeItem : ICloneable
2: {
3: ICloneableTreeItem Clone(object parent);
4: }
The implementation pattern is of course much similar.
Summary
Simple patterns keep simple things simple, and Reflection is not Evil!
August 23rd, 2008 at 11:22 pm
Why do you need CopyTo on the source, why not just have an ‘FinishCloning’ or something on the Cloneable and do the finish-up there? You already have the ‘copy’ of the original members, so you should be able to do everything you have with the results of MemberwiseClone, shouldn’t you? The only thing I can think of is if you have some sort of object graph manipulation that you need to do that needs the ‘original object’ pointer.
August 23rd, 2008 at 11:33 pm
You are right; it is still simpler and more natural! I did not think about it.
Whether or not we give the original object is more a matter of preference, however. In the most general case, we could want this point to the source object. However, we can easily add/remove this parameter using refactoring tools, so it is not that an important decision.
What is more important is the decision whether to have the FinishCloning on the source or on the target, because then it is more difficult to refactor. And you are maybe right on that.
-gael
August 25th, 2008 at 7:42 pm
I’ve used a pattern like this for some year. It’s simple but hide some pitfall:
If the new copy must have the same value for a field or a new copy of that is a semantic issue ( the clone of a car must refer to the same factory or model but I must clone the car’s wheels).
Sometime programmers adding a field to a class forgot to add cloning code to the CopyTo method and so the field of the new object refer to the same of the original one).
All seems to work but one day somebody change the colour of a wheel … oops a lot of cars now have white wheels … this can be a very subtle bug to find.
Now I’ve changed to a Copy Constructor pattern and the Clone method of each class call the copy constructor.
In this way ( if someone forgot to clone a field in the constructor ) I got an Object reference exception at runtime, and not a code that tell “all is ok” and hide many serious mistake.
August 27th, 2008 at 12:37 am
Wouldn’t it better to have this clonning methods as a separate helper class, rather than having all classes inherits from some particular class (Clonable). And additionaly if one ever need to override the FinishClone functionality, they can always implement optional interface (ICloneFinishable). Thus completely eliminates the need to inherit any base class.
September 9th, 2008 at 1:34 pm
@ Mauro: You say that programmers forget to copy a field in CopyTo. It’s true; it’s why cloning should be implemented generically using code generation, reflection, or AOP. But there should be a way to override the automated cloning if it is not adequate for some minority of classes.
@ Hendry: I would say there is no universal rule here. In some cases all your business classes will have a common ancestor, so why bother? The problem with .NET interfaces is that they cannot have “protected” visibility. So it may cause a leaking encapsulation, although it’s maybe not always important.