By implementing the ICloneable interface, you enable a copy of an object to be made. ICloneable defines only one method, Clone( ), which is shown here:
object Clone( )
This method makes a copy of the invoking object. How you implement Clone( ) determines how the copy is made. In general, there are two types of copies: deep and shallow. When a deep copy is made, the copy and original are completely independent. Thus, if the original object contained a reference to another object O, then a copy of O will also be made. In a shallow copy, members are copied, but objects referred to by members are not. If an object refers to some other object O, then after a shallow copy, both the copy and the original will refer to the same O, and any changes to O affect both the copy and the original. Usually, you will implement Clone( ) so that it performs a deep copy. Shallow copies can be made by using MemberwiseClone( ), which is defined by Object.
Here is an example that illustrates ICloneable. It creates a class called Test that contains a reference to a class called X. Test uses Clone( ) to create a deep copy.
// Demonstrate ICloneable. using System; class X { public int a; public X(int x) { a = x; } } class Test : ICloneable { public X o; public int b; public Test(int x, int y) { o = new X(x); b = y; } public void show(string name) { Console.Write(name + " values are "); Console.WriteLine("o.a: {0}, b: {1}", o.a, b); } // Make a deep copy of the invoking object. public object Clone() { Test temp = new Test(o.a, b); return temp; } } class CloneDemo { public static void Main() { Test ob1 = new Test(10, 20); ob1.show("ob1"); Console.WriteLine("Make ob2 a clone of ob1."); Test ob2 = (Test) ob1.Clone(); ob2.show("ob2"); Console.WriteLine("Changing ob1.o.a to 99 and ob1.b to 88."); ob1.o.a = 99; ob1.b = 88; ob1.show("ob1"); ob2.show("ob2"); } }
The output is shown here:
ob1 values are o.a: 10, b: 20 Make ob2 a clone of ob1. ob2 values are o.a: 10, b: 20 Changing ob1.o.a to 99 and ob1.b to 88. ob1 values are o.a: 99, b: 88 ob2 values are o.a: 10, b: 20
As the output shows, ob2 is a clone of ob1, but ob1 and ob2 are completely separate objects. Changing one does not affect the other. This is accomplished by allocating a new X object for the copy and giving it the same value as the X object in the original.
To implement a shallow copy, simply have Clone( ) call MemberwiseClone( ) defined by Object. For example, try changing Clone( ) in the preceding program as shown here:
// Make a shallow copy of the invoking object. public object Clone() { Test temp = (Test) MemberwiseClone(); return temp; }
After you make this change, the output of the program will look like this:
ob1 values are o.a: 10, b: 20 Make ob2 a clone of ob1. ob2 values are o.a: 10, b: 20 Changing ob1.o.a to 99 and ob1.b to 88. ob1 values are o.a: 99, b: 88 ob2 values are o.a: 99, b: 20
Notice that o in ob1 and o in ob2 both refer to the same X object. Changing one affects both. Of course, the int field b in each is still separate because the value types are not accessed via references.