ICloneable


Jump to: navigation, search
Exam Prep. Guides
Exam 70-536 Study Guide

1. Types and collections

2. Process, threading,…
3. Embedding features
4. Serialization, I/O
5. .NET Security
6. Interop., reflection,…
7. Global., drawing, text

edit

Contents


The System.ICloneable interface defines a method of cloning—copying—to create a new instance of a class with the identical value as an existing instance.

There are two ways to clone an instance:

  1. Shallow copy - may be linked to data shared by both the original and the copy
  2. Deep copy - contains the complete encapsulated data of the original object

A shallow copy is by far the easiest way to clone your class. This can be achieved with the MemberwiseClone method inherited by all classes from Object. However, you may find that this is not exactly what you want.


Syntax

Declaration syntax

[ComVisibleAttribute(true)] 
public interface ICloneable

Method syntax

The Clone method creates a new object—a copy of the current instance.

Object Clone ()

Returns a new object that is a copy of the current instance.


Example scenario

To illustrate the differences between deep and shallow copies, the following scenario has been created. In addition to a string variable, a Job class contains a reference to a Duties class.

Job class

class Job
{
   string m_JobName;
   Duties m_Duties;
 
   public Job(string jobName)
   {
      m_JobName = jobName;
   }
 
   public string getJobName()
   {
      return m_JobName;
   }
 
   public void setDuties(string DutyA, string DutyB)
   {
      m_Duties = new Duties(DutyA, DutyB);
   }
 
   public Duties getDuties()
   {
      return m_Duties;
   }
}

Duties class

class Duties
{
   string m_DutyA;
   string m_DutyB;
 
   public Duties(string dutyA, string dutyB)
   {
      m_DutyA = dutyA;
      m_DutyB = dutyB;
   }
 
   public string getDutyA()
   {
      return m_DutyA;
   }
 
   public string getDutyB()
   {
      return m_DutyB;
   }
}

Person class

class Person : ICloneable
{
   string m_Name;
   int m_Age;
   Job m_CurrentJob;
 
   public Person(string name, int age)
   {
      m_Name = name;
      m_Age = age;
   }
 
   public void getJob(Job jobName)
   {
      m_CurrentJob = jobName;
   }
 
   public Job getCurrentJob()
   {
      return m_CurrentJob;
   }
 
   public override string ToString()
   {
      return this.m_Name;
   }
 
   public object Clone()
   {
      return this.MemberwiseClone();      // call clone method
   }
}

ICloneableExample class

class ICloneableExample
{
   static void Main()
   {
      Job swimPool = new Job(" Swimming Pool Attendant ");
      swimPool.setDuties("rescue swimmers in difficulty ", 
         "water quality control ");
   
      Person nicola = new Person("Nicola", 23);
      nicola.getJob(swimPool);
 
      Person nicolaClone = (Person)nicola.Clone();      // clone
 
      Console.WriteLine("(Nicola)");
      printInfo(nicola);
      Console.WriteLine("(NicolaClone)");
      printInfo(nicolaClone);
 
      Console.WriteLine("\r\n *NicolaClones' Job Duties change* \r\n");
      nicolaClone.getCurrentJob().setDuties("Clean Pool", 
         "Welcome Customers");
 
      Console.WriteLine("(Nicola)");
      printInfo(nicola);
      Console.WriteLine("(NicolaClone)");
      printInfo(nicolaClone);
   }
   
   static void printInfo(Person person)
   {
      Console.WriteLine("Name: {0}", person);
      Console.WriteLine("   Job: {0}", 
         person.getCurrentJob().getJobName());
      Console.WriteLine("      Duty A: {0}", 
         person.getCurrentJob().getDuties().getDutyA());
      Console.WriteLine("      Duty B: {0}", 
         person.getCurrentJob().getDuties().getDutyB());
   }
}

MemberwiseClone

The Person.Clone method simply calls the Object.MemberwiseClone method to make a shallow copy of the instance.


 ICloneableExample (program output)
(Nicola)
Name: Nicola
    Job: Swimming Pool Attendant 
       Duty A: rescue swimmers in difficulty 
       Duty B: water quality control 
(NicolaClone)
Name: Nicola
    Job: Swimming Pool Attendant 
       Duty A: rescue swimmers in difficulty 
       Duty B: water quality control 

  *NicolaClones' Job Duties change*

(Nicola)
Name: Nicola
    Job: Swimming Pool Attendant 
        Duty A: Clean Pool 
        Duty B: Welcome Customers 
(NicolaClone)
Name: Nicola
    Job: Swimming Pool Attendant 
        Duty A: Clean Pool 
        Duty B: Welcome Customers

Notice that the duties of both the original and the clone have changed.

Shallow copy example

What has been created above is called a shallow copy. Meaning, only the top level class is actually copied to the heap. If that top level class contains any references to instances, only the references will be copied. So, original and clone are exactly the same object, hence changing duties for one copy changes both copies.

To demonstrate this further, a shallow change can be done—for example: changing the person’s name.

Person Class - Additional method

public void changeName(string name)
{
   m_Name = name;
}

Updated Main method

static void Main()
{
   Job swimPool = new Job(" Swimming Pool Attendant ");
   swimPool.setDuties("rescue swimmers in difficulty ", "water quality control ");
 
   Person nicola = new Person("Nicola", 23);
   nicola.getJob(swimPool);
 
   Person nicolaClone = (Person)nicola.Clone();
 
   Console.WriteLine("(Nicola)");
   printInfo(nicola);
   Console.WriteLine("(NicolaClone)");
   printInfo(nicolaClone);
 
   Console.WriteLine("\r\n *NicolaClone changes name* \r\n");
   nicolaClone.changeName("Becky");
 
   Console.WriteLine("(Nicola)");
   printInfo(nicola);
   Console.WriteLine("(NicolaClone)");
   printInfo(nicolaClone);
}


 ICloneableExample (program output)
(Nicola)
Name: Nicola
    Job: Swimming Pool Attendant 
        Duty A: rescue swimmers in difficulty 
        Duty B: water quality control 
(NicolaClone)
Name: Nicola
    Job: Swimming Pool Attendant 
        Duty A: rescue swimmers in difficulty 
        Duty B: water quality control 

  *NicolaClone changes name*

(Nicola)
Name: Nicola
    Job: Swimming Pool Attendant 
        Duty A: rescue swimmers in difficulty 
        Duty B: water quality control 
(NicolaClone)
Name: Becky
    Job: Swimming Pool Attendant 
        Duty A: rescue swimmers in difficulty 
        Duty B: water quality control

Notice how the name has changed. Although a string is a reference type it has been copied due to it being a system type.

Deep copy example

In a deep copy, the clone method requires additional code to create new instances of any classes referenced by the original.

This example uses the first version of Main.

Updated Clone method

public object Clone()
{
   Person Copy = new Person(this.m_Name, this.m_Age);
   Job CopyJob = new Job(this.m_CurrentJob.getJobName());
   CopyJob.setDuties (this.m_CurrentJob.getDuties().getDutyA(),
      this.m_CurrentJob.getDuties().getDutyB());
   Copy.getJob(CopyJob);
 
   return Copy;
 
}


 ICloneableExample (program output)
(Nicola)
Name: Nicola
    Job: Swimming Pool Attendant 
        Duty A: rescue swimmers in difficulty 
        Duty B: water quality control 
(NicolaClone)
Name: Nicola
    Job: Swimming Pool Attendant 
        Duty A: rescue swimmers in difficulty 
        Duty B: water quality control 


  *NicolaClones' Job Duties change*

(Nicola)
Name: Nicola
    Job: Swimming Pool Attendant 
        Duty A: rescue swimmers in difficulty 
        Duty B: water quality control 
(NicolaClone)
Name: Nicola
    Job: Swimming Pool Attendant 
        Duty A: Clean Pool 
        Duty B: Welcome Customers

Design to be cloned

In the previous example, all cloning was done by just one class, the Person class. This means that the Person class must understand how to clone not only itself, but also a Job and the Jobs' Duties. Although this does mean that all cloning is done in one place it does not support the 'OO' principal of encapsulation. That is to say that if a class could be used in a different project then it should be able to work without the user class understanding the inner workings. To support this each class can have its own Clone method.

The following shows how a class can be designed or changed to allow the use of class cloning which in turn promotes encapsulation.

Clone-unfriendly example

class Job : ICloneable
{
   string m_JobName;
   Duties m_Duties;
 
   public Job(string jobName)
   {
      m_JobName = jobName;
   }
 
   public string getJobName()
   {
      return m_JobName;
   }
 
   /*********************************************************
   * This should be phased out ASAP!                        *
   * If Duties change this will have to be changed          *
   * This goes against encapsulation rules where public     *
   * methods should always remain the same.                 *
   *********************************************************/
   public void setDuties(string DutyA, string DutyB)
   {
      m_Duties = new Duties(DutyA, DutyB);
   }
 
   public Duties getDuties()
   {
      return m_Duties;
   }
 
   public object Clone()
   {
      Job copy = new Job(this.m_JobName);
 
      /*****************************************************************
      * The Job class must understand how to set duties.               *
      * If Duties change then the Job class will also have to change!  *
      *****************************************************************/
      copy.setDuties(this.m_Duties.getDutyA(),this.m_Duties.getDutyB());
 
      return copy;
   }
 
}

Clone-friendly example -

class Job : ICloneable
{
   string m_JobName;
   Duties m_Duties;
 
 
   public Job(string jobName)
   {
      m_JobName = jobName;
   }
 
   public string getJobName()
   {
      return m_JobName;
   }
 
   /*********************************************************
   * Overload the method, this allows the existing          *
   * method to remain for older code which uses it.         *
   *                                                        *
   * This design works even if the Duties class is changed! *
   *********************************************************/
   public void setDuties(Duties duties)
   {
      m_Duties = duties;
   }
 
   public Duties getDuties()
   {
      return m_Duties;
   }
 
   public object Clone()
   {
      Job copy = new Job(this.m_JobName);
 
      /******************************************************
      * Duties knows more about itself than any other class *
      * So let it clone itself!                             *
      ******************************************************/
      copy.setDuties((Duties) this.m_Duties.Clone());
 
      return copy;
   }
 
}

The final main classes

The clone friendly classes follow:

class Person : ICloneable
{
   string m_Name;
   int m_Age;
   Job m_CurrentJob;
 
   public Person(string name, int age)
   {
      m_Name = name;
      m_Age = age;
   }
 
   public void getJob(Job jobName)
   {
      m_CurrentJob = jobName;
   }
 
   public Job getCurrentJob()
   {
      return m_CurrentJob;
   }
 
   public void changeName(string name)
   {
      m_Name = name;
   }
 
   public override string ToString()
   {
      return this.m_Name;
   }
 
   #region ICloneable Members
 
   public object Clone()
   {
      Person Copy = new Person(this.m_Name, this.m_Age);
      Copy.getJob((Job) this.getCurrentJob().Clone());
 
      return Copy;
   }
   #endregion
}
class Job : ICloneable
{
   string m_JobName;
   Duties m_Duties;
 
   public Job(string jobName)
   {
      m_JobName = jobName;
   }
 
   public string getJobName()
   {
      return m_JobName;
   }
 
   public void setDuties(Duties duties)
   {
      m_Duties = duties;
   }
 
   public Duties getDuties()
   {
      return m_Duties;
   }
 
   #region ICloneable Members
 
   public object Clone()
   {
      Job copy = new Job(this.m_JobName);
      copy.setDuties((Duties) this.m_Duties.Clone());
 
      return copy;
   }
 
   #endregion
}
class Duties: ICloneable
{
   string m_DutyA;
   string m_DutyB;
 
   public Duties(string dutyA, string dutyB)
   {
      m_DutyA = dutyA;
      m_DutyB = dutyB;
   }
 
   public string getDutyA()
   {
      return m_DutyA;
   }
 
   public string getDutyB()
   {
      return m_DutyB;
   }
 
   #region ICloneable Members
 
   public object Clone()
   {
      Duties copy = new Duties(this.m_DutyA, this.m_DutyB);
      return copy;
   }
 
   #endregion
}


Example

Note that only a slight change had to be made in the way that duties are set due to phasing out the clone-unfriendly method.

static void Main()
{
   Job swimPool = new Job(" Swimming Pool Attendant ");
   swimPool.setDuties (
      new Duties("rescue swimmers in difficulty ", 
                 "water quality control "));
 
   Person nicola = new Person("Nicola", 23);
   nicola.getJob(swimPool);
 
   Person nicolaClone = (Person)nicola.Clone();
 
   Console.WriteLine("(Nicola)");
   printInfo(nicola);
   Console.WriteLine("(NicolaClone)");
   printInfo(nicolaClone);
 
   Console.WriteLine("\r\n *NicolaClones' Job Duties change* \r\n");
   nicolaClone.getCurrentJob().setDuties (
      new Duties("Clean Pool", "Welcome Customers"));
   
   Console.WriteLine("(Nicola)");
   printInfo(nicola);
   Console.WriteLine("(NicolaClone)");
   printInfo(nicolaClone);
}
 
static void printInfo(Person person)
{
   Console.WriteLine("Name: {0}", person);
   Console.WriteLine("   Job: {0}", 
      person.getCurrentJob().getJobName());
   Console.WriteLine("      Duty A: {0}", 
      person.getCurrentJob().getDuties().getDutyA());
   Console.WriteLine("      Duty B: {0}", 
      person.getCurrentJob().getDuties().getDutyB());
}

MSDN references


Previous_Page_.gif Next_Page_.gif

Share this page
  • del.icio.us
  • Facebook
  • Google+
  • StumbleUpon