The ICloneable interface is easy enough to implement when using a flat class, but if you have a more involved class structure with lists of subclasses, it quickly becomes apparent that the class is not being properly cloned. This article will lay out a very simple class structure hierarchy, and then go about demonstrating how to clone the uppermost class.
Creating the Class Hierarchy
The first thing we need to do is create a simple class hierarchy with which to demonstrate cloning. For this, I will use the example of a house. The house has a list of walls, and each wall in turn will hold a list of windows, so we end up with a data structure as follows:
The Window Class
When developing this hierarchy, we need to start with the lowest class level, which in this case is the Window class. All we need to do in the clone function here is create a MemberwiseClone of the window itself, and return it as an object:
/// <summary> /// The clone function for the window only needs to clone the window itself /// as there are no additional lists within here. This is the lowest level class. /// </summary> public object Clone() { Window WindowClone = (Window)MemberwiseClone(); return WindowClone; }
The Wall Class
Once the Window class is fully implemented, we can create our Wall class. The clone function of this however is more involved:
/// <summary> /// The clone function for the wall must first clone the wall itself, then clear /// out the list within the clone, finally repopulating the list with clones of /// the windows within the current class. /// </summary> public object Clone() { Wall WallClone = (Wall)MemberwiseClone(); WallClone.ClearWindows(); foreach (Window window in _Windows) { WallClone.AddWindow((Window)window.Clone()); } return WallClone; }
In order to successfully clone our class we need to clone each subclass individually, as the ICloneable interface will not clone any lists we have. To do this, three steps must be taken:
- Clone the existing class with a standard MemberwiseClone
- Clear all of the subclass instances out of the newly cloned class
- Re-add each subclass individually to the new cloned class, by cloning it out of the existing class
So each time a wall is cloned, the clone action will clone each window in to the new wall.
The House Class
When cloning a house, similar to the Wall class, we need to clone each subclass belonging to our main class:
/// <summary> /// The clone function for the house must first clone the house itself, then /// clear out the list within the clone, finally repopulating the list with /// clones of the walls within the current class. /// </summary> public object Clone() { House HouseClone = (House)MemberwiseClone(); HouseClone.ClearWalls(); foreach (Wall wall in _Walls) { HouseClone.AddWall((Wall)wall.Clone()); } return HouseClone; }
Again, the following three steps must be taken:
- Clone the existing class with a standard MemberwiseClone
- Clear all of the subclass instances out of the newly cloned class
- Re-add each subclass individually to the new cloned class, by cloning it out of the existing class
So each time a house is cloned, the clone action will clone each wall in to the new house.
Summary
Whenever we want to clone a class which contains subclasses, we also need to clone our subclasses or they are simply references within the new cloned class.
The full process of cloning a house simply by using the Clone() command will loop through and clone each wall belonging to that house, which in turn will loop through and clone each window belonging to each wall. The result is a completely separated clone of the original parent class.
Why would we want to do this? In a winforms application, we may want to give the user the ability to edit a complex class. With this hierarchical cloning, we can quickly make a new version of our class for the user to edit, and have the ability to revert back to a previous version (undo/redo). We can quickly clone our class structure as many times as we like, then trash all of the clones if we want to cancel changes, or overwrite the original class with one of the clones to save changes.