Cloning Objects


Cloning objects is required to facilitate working on copies of an object without affecting the original and its contents. Systems use cloning in many different ways but generally it will be used to initialize an object with a default state. This section will focus on the different ways we can clone an object and how it relates back to object construction.

Using the Copy Constructor

The need for cloning objects has led to the evolution of the copy constructor, which is a constructor that takes its own class type as a parameter. In this way, the class instance parameter can be used to populate the field values of the current instance of the object.

The Teacher class written below includes a copy constructor, where in every new instance of the class we can pass a single object reference that will initialize all new class instances of the Teacher class. There are many different approaches that can be taken here; either a class instance can be used to set initialization for all subsequent class instances, or in the case of the Teacher class we may want to create several class instances, one for each department and pass each one to the copy constructor of a subset of the Teacher objects we wish to create. We don't have to use the constructor as a template but can use it simply to copy certain aspects of a class, which are essential to initialization with default state for the particular object.

     public class Teacher     {       private int grade;       private int salary;       private string dept = String.Empty;       public Teacher() {}       public Teacher(int grade, int salary, string dept)       {         this.grade = grade;         this.salary = salary;         this.dept = dept;       }       public Teacher(Teacher teach)       {         grade = teach.grade;         salary = teach.salary;         dept = teach.dept;       }     } 

To invoke the copy constructor, we can use the following in a Main() method. The code file for this example is called copy_ctors.cs. This will first create a Teacher object with the default values that we intend to copy followed by passing the object reference to the copy constructor and using the values to populate the fields in the asyet-not-initialized class instance.

         Teacher teach1 = new Teacher(3, 23000, "IT");         Teacher teach2 = new Teacher(teach1); 

With the introduction of reflection into the .NET Framework it is easy to conceive of developing generic copy constructors that can be used to reflect on a type and extract the values of a particular object by identifying the property values available in that type. We can use reflection to dynamically find everything out about the public interface of the type, and also to add the values to the current instance of the class. In order to use reflection we must either add public property values to the object or declare the fields as public. We have opted for the former in this case:

     public int Grade { get { return grade; } set { grade = value; }}     public int Salary { get { return salary; } set { salary = value; }}     public string Dept { get { return dept; } set { dept = value; }} 

The current implementation of the Teacher class copy constructor could be replaced by the following. This would enable a further abstraction allowing all the property values to be obtained from one instance of the class and used to populate the property values in the current instance. In this way the interface on the Teacher class can change (that is, we can add more properties to the class and not update any more code since the rest is relatively dynamic) with no updates to the copy constructor, as long as there are publicly accessible property or field values. The classes used below are available by referencing the System.Reflection namespace.

     public Teacher(Teacher teach)     {       PropertyInfo[] pInfoArray = teach.GetType().GetProperties();       foreach(PropertyInfo pInfo in pInfoArray)       {         Type ty = pInfo.PropertyType;         object myvalue =             teach.GetType().InvokeMember(pInfo.Name,                                          BindingFlags.GetProperty, null,                                          teach, null);         teach.GetType().InvokeMember(pInfo.Name,                                      BindingFlags.SetProperty, null,                                      this, new object[]{myvalue});       }     } 

Invoking the get property accessors on the new Teacher class instance will reveal that the values have been copied from one object to the other without specifically naming a field or property.

         Console.WriteLine("Details:{0}, {1}, {2}",             teach2.Grade, teach2.Dept, teach2.Salary); 

The ICloneable Interface

Many classes within the .NET Framework implement the ICloneable interface, which can be used instead of a copy constructor to obtain a clone of an object. ICloneable provides a single Clone() method, which must be implemented. We can change the definition of the Teacher class by implementing the ICloneable class.

     public class Teacher : ICloneable 

We now have to implement the Clone() method to return a copied instance of the Teacher class. To comply with the ICloneable interface, the Clone() method must return an object type. Using ICloneable can prove very useful; since we understand the nature of the contract with ICloneable we can build classes that can test whether this supports cloning; that is, the ICloneable interface and then cast it in a generic manner. An object can then be returned by the application through a method that accepts any ICloneable interface type as opposed to a concrete class type.

     public object Clone()     {       return new Teacher(grade, salary, dept) as object;     } 

Calling the Clone() method will return us a Teacher object of type object that we can cast back to a Teacher.

     Teacher teach3 = (Teacher)teach2.Clone(); 

The above is an example of a deep copy. This is somewhat more expensive than an equivalent shallow copy. A deep copy will result in the copying of a fully populated object, which contains its own values of other reference and value types. A shallow copy, however, is a copy of all the references in one object, so in effect it's a copy of references to types that were created, initialized and used within another object. It doesn't have its own copy of these values and shares them through the references with all other objects that reference them. Each reference to a type will increase the reference count to that type by one, and hence overusing this will prohibit objects being destroyed by the GC. Every object inherits a method from System.Object called MemberWiseClone() that will return a shallow copy of the object. The main limitation with using a shallow copy of an object is that if any of the values are changed in the source object, then as the second copy references all the same values in memory, all of the changes are implicit and carried over to the cloned object. A member-wise clone causes a shallow copy but certain things are copied by value, like all the value types.

     public object Clone()     {       return this.MemberwiseClone();     } 

Deep cloning can be a very expensive operation and should be avoided unless necessary, since a by-value copy of all the member fields will occur. On a grand scale this can be very expensive involving the creation of many objects, though in reality we may use combinations of the two types of cloning. Some objects that contain references to many other objects or object collection may function well with deep copies.




C# Class Design Handbook(c) Coding Effective Classes
C# Class Design Handbook: Coding Effective Classes
ISBN: 1590592573
EAN: 2147483647
Year: N/A
Pages: 90

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net