Now, before you get too excited, I’m not suggesting that you implement the all-but-deprecated ICloneable interface. Rather, this post is about how best to implement a method that duplicates an object - such a method is commonly named “Clone”.
If you like, you can skip the long-winded commentary below and start reading the recommendations at the bottom.
Some would argue that Copy is a better name than Clone, since it avoids the “smell” of ICloneable, but I think Clone is more discoverable. Obviously the patterns of this post can be used regardless of whether you like Clone, Copy, Duplicate, or Replicate.
Any Clone method should document its semantics if they aren’t obvious; in particular, it should be clear whether a “shallow” or a “deep” clone will be used. (A “deep” clone clones its “children”; a “shallow” clone simply copies the references of its children.)
In some circumstances, it may be useful to add a parameter to Clone that can change the behavior. For example, proposals to improve the ICloneable interface included a parameter that would indicate whether a shallow or deep clone is desired. Adding a parameter to the Clone method should be a simple extension to the patterns described below.
The simplest kind of clonable class is a sealed class, because we don’t need to support derived classes. Even in this simple case, the cleanest approach is to delegate the cloning to a private “copy constructor”:
Now, suppose we wanted Vector to be an abstract class so that derived classes could decide how the items are stored. Furthermore, we determine that the length needs to be cached in the base class for performance reasons:
The protected “copy constructor” is provided to simplify the Clone override:
In the ArrayVector implementation above, I’d like the overridden Clone method to be more type-safe - that is, I’d like it to return an ArrayVector instead of a Vector. Unfortunately, while some languages (e.g. C++) allow overrides to return a more-derived class than the method they override, C# does not. Not to be deterred in my quest for type safety, my preferred pattern is to use CloneCore as the overridable and define Clone as a separate non-overridable method in each concrete class.
Incidentally, if you really want to implement ICloneable:
Okay, enough commentary. Here are the recommendations:
There are three parts to a clonable class: (1) the copy constructor, (2) the CloneCore method, and (3) the Clone method.
The copy constructor is always defined, and does all of the copying, being sure to call the base class copy constructor if available. It is always protected (unless the class is sealed, in which case it is private).
The CloneCore and Clone methods are only implemented on non-abstract classes, and always have the same definitions. (Exception: if the root class is abstract, CloneCore is an abstract method on that class.) The CloneCore method uses the copy constructor to clone the instance. The Clone method calls CloneCore and casts the result to the correct type. If the Clone method overloads a base class method, use the “new” keyword.
Clear? No? Let’s try sample code. Consider the clonable classes Base and Derived, where Derived derives from Base. The Base class should always have a protected copy constructor:
If Base is abstract, it only has an abstract CloneCore:
If Base is not abstract, it defines both Clone and CloneCore:
Similarly, the Derived class should always have a copy constructor:
But if Derived is sealed, it will need to be private:
If Derived is abstract, it is done. If Derived is not abstract, it defines both Clone and CloneCore:
If no ancestors have defined a Clone method (e.g. Base is abstract), you’ll have to omit the “new” keyword from the Derived Clone method.
Whew. It feels like I wrote too much, but I’ll just publish and move on. Hope this is useful!
Posted by Ed Ball on June 09, 2008