by Zoran Horvat
It is common requirement in .NET programming to create a cloneable class, i.e. to implement System.ICloneable interface. Another common requirement is to implement a class which would serve as a base class to other classes. It is somewhat less common to implement both requirements at the same class, i.e. to make a class cloneable in such way that classes derived from it are also cloneable.
This article gives guidelines that should be followed when developing base class which implements ICloneable interface.
As will be explained and demonstrated, all implementations should stick to these guidelines:
These guidelines will be explained in more details in following sections.
For guidelines on making decision whether a class should implement ICloneable interface in the first place, please refer to article The ICloneable Controversy: Should a Class Implement ICloneable Or Not? .
As a matter of demonstration, we will create one base class and one derived class. Base class will be called PriceTag, and it will contain randomly generated price (in pounds), rounded to one pound. Its ToString() method will show a correctly formatted price tag.
class PriceTag
{
public PriceTag()
{
_pounds = _rnd.Next(10, 100);
}
public int Pounds
{
get { return _pounds; }
}
public override string ToString()
{
return string.Format("{0} GBP", _pounds);
}
private int _pounds;
protected static Random _rnd = new Random();
}
This little class initializes random price between 10 and 99 pounds (inclusive) so that any instance will differ from 89 out of 90 other instances, unless cloned. And here this class is in action:
static void Main(string[] args)
{
PriceTag pt = new PriceTag();
Console.WriteLine(pt);
}
Output depends on seed taken when random number generator was initialized, and may look like this:
82 GBP
Now let's suppose that we want to make this class cloneable. As simple as it can be, we will just implement System.ICloneable interface so that Clone() method copies contained price into new object:
class PriceTag: ICloneable
{
...
public object Clone()
{
PriceTag pt = new PriceTag();
pt._pounds = _pounds;
return pt;
}
...
}
And these are changes made to Main function to test the Clone() method:
PriceTag pt = new PriceTag();
PriceTag clone = pt.Clone() as PriceTag;
Console.WriteLine(pt);
Console.WriteLine(clone);
Output shows that cloning went fine:
46 GBP
46 GBP
Again, output varies in each execution due to random nature of the class, but both lines show the same price tag as required. If run many times, this code will always produce and print out two exactly the same objects, proving that ICloneable interface is properly implemented.
Now suppose that we want to add pennies to our price tags. That would require deriving a class, named PencePriceTag, which will add new functionality. It will randomly generate pennies at 5p precision so to ensure that any two instances of this class would mostly often show different price. Here is the class:
class PencePriceTag: PriceTag
{
public PencePriceTag()
{
_pence = PriceTag._rnd.Next(20) * 5;
}
public override string ToString()
{
return string.Format("{0}.{1} GBP", Pounds, _pence);
}
private int _pence;
}
Now note that PencePriceTag class has the inherited Clone() method. However, this does not mean that cloning has been implemented in the derived class, because base class's implementation of Clone() method only returns new instance of the base class, as shown in the following test:
PencePriceTag ppt = new PencePriceTag();
object clone = ppt.Clone();
Console.WriteLine(ppt);
Console.WriteLine(clone);
Test code prints the output:
38.40 GBP
38 GBP
This piece of code has created an instance of derived class and then attempted to clone it. Note that Clone() method returns System.Object, and casting it to PencePriceTag will fail because in this case return value would be of type PriceTag. As expected, this code has printed out different objects, showing that cloning has actually failed.
The problem here is not only that derived class does not clone correctly (it does not clone at all), but also that derived class most obviously implies that it is cloneable - it implements ICloneable interface and has the Clone() method. So any user of this code will be invited to clone objects of the derived class PencePriceTag, but then the code will not work! For example, take a method which receives ICloneable objects:
class Program
{
static void CloneAndPrint(ICloneable obj)
{
Console.WriteLine(obj);
Console.WriteLine(obj.Clone());
}
static void Main(string[] args)
{
CloneAndPrint(new PencePriceTag());
}
}
This code compiles well but works bad, which is a bug by definition.
From this demonstration it is obvious that explicit cloning facility must be implemented in every class which is derived from class implementing ICloneable interface. However, there is a fact that base class potentially contains some fields or data structures that are inaccessible to derived class. This may significantly complicate the situation.
Up to this point we have outlined the problem. Once class implementing ICloneable is derived, it automatically results in a fake cloneable class, i.e. a class which implements ICloneable interface but does not clone when asked to. Even more, derived class has no way to change that! The Clone() method is implemented in base class and that cannot be affected by the derived class. (All this time we are assuming worst case, i.e. that we are unable to modify base class.)
To solve the problem in a regular object-oriented manner, author of the base class must be aware that class will be extended by derived classes, and hence must be aware of the possible problems that will be caused by the Clone() method. Hence, two steps must be done, of which first one is cosmetics and the second one is fundamental.
First step is to make the Clone() method virtual. That will allow derived classes to re-implement it as they will:
class PriceTag: ICloneable
{
...
public virtual object Clone()
{
PriceTag pt = new PriceTag();
pt._pounds = _pounds;
return pt;
}
...
}
Now it remains that derived class override the Clone() method and add its own cloning operations. Naive approach might look something like this:
class PencePriceTag: PriceTag
{
...
public override object Clone()
{
PencePriceTag ppt = new PencePriceTag();
ppt._pence = this._pence;
return ppt;
}
...
}
Values printed by this code point out the mistake:
54.55 GBP
75.55 GBP
Pennies have been copied right, but pounds were not copied at all (but rather generated randomly again). This is because only derived implementation of the Clone() method has been executed. Members of the base class have been initialized by the base class's default constructor, instead of copied from the instance being cloned.
Consequently, we come to the fundamental step which would rectify the problem, and that is to add a facility to the base class which will allow derived class to copy base members into the cloned object. Most natural way to do so is to implement copy constructor in the base class (this is generally advisable to do whether class being cloneable or not). Copy constructor would be in charge of performing the deep copy of one instance into newly created instance.
Similarly, derived class would have own copy constructor, which effectively performs the cloning. Clone method in both classes would now just create new instance of the same class using its copy constructor. Here is the implementation:
class PriceTag: ICloneable
{
...
public PriceTag(PriceTag pt)
{
_pounds = pt._pounds;
}
...
public virtual object Clone()
{
return new PriceTag(this);
}
...
}
class PencePriceTag: PriceTag
{
...
public PencePriceTag(PencePriceTag ppt): base(ppt)
{
_pence = ppt._pence;
}
...
public override object Clone()
{
return new PencePriceTag(this);
}
...
}
Observe that the derived class is coded with exactly the same guidelines as the base class - Clone() method remains virtual and there is a copy constructor implemented. This is because we cannot say that someone is not going to derive yet another class from this one (e.g. the one that implements multiple currencies). That class would certainly like to be able to clone itself correctly once things come to that point.
Now we can safely invoke previously introduced CloneAndPrint() method, which is now going to create full deep copy of the derived class, including base class members. Sample output may look like this:
84.20 GBP
84.20 GBP
Keep on mind that copy constructor technique ensures that base class members will be deep-copied before derived class members, which is extremely important. Initialization of derived class members may depend on base class members and order of initializations must be strictly obeyed.
Whenever a class implements System.ICloneable interface it implements Clone() method which performs a deep copy operation on objects of that class. However, derived classes may be left in state in which they do contain inherited Clone() method, but cannot implement it correctly. In order to avoid this unfortunate situation, base class must either forbid deriving (by using sealed C# keyword, or NotInheritable in VisualBasic.NET), or explicitly support cloneability in derived classes. This is done in these steps:
Disobeying these rules means that derived class will not be able to override inherited cloning ability and hence Clone() method invoked on derived class would return incorrect result.
For more information about the ICloneable interface please refer to article The ICloneable Controversy: Should a Class Implement ICloneable Or Not? .
If you wish to learn more, please watch my latest video courses
In this course, you will learn the basic principles of object-oriented programming, and then learn how to apply those principles to construct an operational and correct code using the C# programming language and .NET.
As the course progresses, you will learn such programming concepts as objects, method resolution, polymorphism, object composition, class inheritance, object substitution, etc., but also the basic principles of object-oriented design and even project management, such as abstraction, dependency injection, open-closed principle, tell don't ask principle, the principles of agile software development and many more.
More...
In this course, you will learn how design patterns can be applied to make code better: flexible, short, readable.
You will learn how to decide when and which pattern to apply by formally analyzing the need to flex around specific axis.
More...
This course begins with examination of a realistic application, which is poorly factored and doesn't incorporate design patterns. It is nearly impossible to maintain and develop this application further, due to its poor structure and design.
As demonstration after demonstration will unfold, we will refactor this entire application, fitting many design patterns into place almost without effort. By the end of the course, you will know how code refactoring and design patterns can operate together, and help each other create great design.
More...
In four and a half hours of this course, you will learn how to control design of classes, design of complex algorithms, and how to recognize and implement data structures.
After completing this course, you will know how to develop a large and complex domain model, which you will be able to maintain and extend further. And, not to forget, the model you develop in this way will be correct and free of bugs.
More...
Zoran Horvat is the Principal Consultant at Coding Helmet, speaker and author of 100+ articles, and independent trainer on .NET technology stack. He can often be found speaking at conferences and user groups, promoting object-oriented and functional development style and clean coding practices and techniques that improve longevity of complex business applications.