16.2 Implement a Cloneable Type


Problem

You need to create a custom type that provides a simple mechanism for programmers to create copies of type instances.

Solution

Implement the System.ICloneable interface.

Discussion

When you assign one value type to another, you create a copy of the value. There's no link between the two values ”a change to one won't affect the other. However, when you assign one reference type to another (excluding strings, which receive special treatment by the runtime), you don't create a new copy of the reference type. Instead, both reference types refer to the same object, and changes to the value of the object are reflected in both references. To create a true copy of a reference type, you must clone the object to which it refers.

The ICloneable interface identifies a type as cloneable and declares the Clone method as the mechanism through which you obtain a clone of an object. The Clone method takes no arguments and returns a System.Object , regardless of the implementing type. This means that once you clone an object, you must explicitly cast the clone to the correct type.

The approach you take to implementing the Clone method for a custom type depends on the data members declared within the type. If the custom type contains only value-type ( int , byte , and so on) and System.String data members, you can implement the Clone method by instantiating a new object and setting its data members to the same values as the current object. The Object class (from which all types derive) includes the protected method MemberwiseClone , which automates this process. Here is an example that shows a simple class named Employee , which contains only string members. Therefore, the Clone method relies on the inherited MemberwiseClone method to create a clone.

 using System; public class Employee : ICloneable {          public string Name;     public string Title;     // Simple Employee constructor     public Employee(string name, string title) {         Name = name;         Title = title;     }              // Create a clone using the Object.MemberwiseClone method because the     // Employee class contains only string references     public object Clone() {                  return MemberwiseClone();     } } 

If your custom type contains reference-type data members, you must decide whether your Clone method will perform a shallow copy or a deep copy . A shallow copy means that any reference-type data members in the clone will refer to the same objects as the equivalent reference-type data members in the original object. A deep copy means that you must create clones of the entire object graph so that the reference-type data members of the clone refer to physically independent copies ( clones ) of the objects referenced by the original object.

A shallow copy is easy to implement using the MemberwiseClone method just described. However, a deep copy is often what programmers expect when they first clone an object ”but it's rarely what they get. This is especially true of the collection classes in the System.Collections namespace, which all implement shallow copies in their Clone methods . Although it would often be useful if these collections implemented a deep copy, there are two key reasons why types (especially generic collection classes) do not implement deep copies:

  • Creating a clone of a large object graph is processor intensive and memory intensive .

  • Generic collections can contain wide and deep object graphs consisting of any type of object. Creating a deep copy implementation to cater for such variety isn't feasible because some objects in the collection might not be cloneable, and others might contain circular references, which would send the cloning process into an infinite loop.

For strongly typed collections in which the nature of the contained elements are understood and controlled, a deep copy can be a very useful feature; for example, the System.Xml.XmlNode implements a deep copy in its Clone method. This allows you to create true copies of entire XML object hierarchies with a single statement.

Tip  

If you need to clone an object that does not implement ICloneable but is serializable, you can often serialize and then deserialize the object to achieve the same result as cloning. However, be aware that the serialization process might not serialize all data members (as discussed in recipe 16.1). Likewise, if you create a custom serializable type, you can potentially use the serialization process just described to perform a deep copy within your ICloneable.Clone method implementation. To clone a serializable object, use the class System.Runtime.Serialization.Formatters.Binary.BinaryFormatter to serialize the object to, and then deserialize the object from, a System.IO.MemoryStream object.

The Team class shown in the following listing contains an implementation of the Clone method that performs a deep copy. The Team class contains a collection of Employee objects, representing a team of people. When you call the Clone method of a Team object, the method creates a clone of every contained Employee object and adds it to the cloned Team object. The Team class provides a private constructor to simplify the code in the Clone method ”the use of constructors is a common approach to simplify the cloning process. The file CloneableExample.cs in the sample code for this chapter contains the Team and Employee classes. The file also provides a Main method that demonstrates the effect of making a deep copy.

 using System; using System.Collections; public class Team : ICloneable {          public ArrayList TeamMembers = new ArrayList();          public Team() {     }     // Private constructor called by the Clone method to create a new Team     // object and populate its ArrayList with clones of Employee      // objects from a provided ArrayList     private Team(ArrayList members) {                  foreach (Employee e in members) {                          TeamMembers.Add(e.Clone());         }     }          // Adds an Employee object to the Team     public void AddMember(Employee member) {              TeamMembers.Add(member);     }     public object Clone() {                  // Create a deep copy of the team by calling the private Team         // constructor and passing the ArrayList containing team members         return new Team(this.TeamMembers);                  // The following command would create a shallow copy of the Team         // return MemberwiseClone();     }         } 



C# Programmer[ap]s Cookbook
C# Programmer[ap]s Cookbook
ISBN: 735619301
EAN: N/A
Year: 2006
Pages: 266

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