Tải bản đầy đủ - 0 (trang)
13-2. Implement a Cloneable Type

13-2. Implement a Cloneable Type

Tải bản đầy đủ - 0trang

CHAPTER 13 ■ COMMONLY USED INTERFACES AND PATTERNS



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 implement the Clone method for a custom type depends on the data

members declared within the type. If the custom type contains only value-type data members (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.

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 memoryintensive.







General-purpose collections can contain wide and deep object graphs consisting

of any type of object. Creating a deep-copy implementation to cater to such

variety is not 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, 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 13-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.



627



www.it-ebooks.info



CHAPTER 13 ■ COMMONLY USED INTERFACES AND PATTERNS



The Code

The following example demonstrates various approaches to cloning. The simple class Employee contains

only string and int members, and so relies on the inherited MemberwiseClone method to create a clone.

The Team class 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 simplifying the cloning process.

using System;

using System.Text;

using System.Collections.Generic;

namespace Apress.VisualCSharpRecipes.Chapter13

{

public class Employee : ICloneable

{

public string Name;

public string Title;

public int Age;

// Simple Employee constructor.

public Employee(string name, string title, int age)

{

Name = name;

Title = title;

Age = age;

}

// Create a clone using the Object.MemberwiseClone method because the

// Employee class contains only string and value types.

public object Clone()

{

return MemberwiseClone();

}

// Returns a string representation of the Employee object.

public override string ToString()

{

return string.Format("{0} ({1}) - Age {2}", Name, Title, Age);

}

}

public class Team : ICloneable

{

// A List to hold the Employee team members.

public List TeamMembers =

new List();



628



www.it-ebooks.info



CHAPTER 13 ■ COMMONLY USED INTERFACES AND PATTERNS



public Team()

{

}

// Private constructor called by the Clone method to create a new Team

// object and populate its List with clones of Employee objects from

// a provided List.

private Team(List members)

{

foreach (Employee e in members)

{

// Clone the individual employee objects and

// add them to the List.

TeamMembers.Add((Employee)e.Clone());

}

}

// Adds an Employee object to the Team.

public void AddMember(Employee member)

{

TeamMembers.Add(member);

}

// Override Object.ToString to return a string representation of the

// entire Team.

public override string ToString()

{

StringBuilder str = new StringBuilder();

foreach (Employee e in TeamMembers)

{

str.AppendFormat(" {0}\r\n", e);

}

return str.ToString();

}

// Implementation of ICloneable.Clone.

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();

}

}



629



www.it-ebooks.info



CHAPTER 13 ■ COMMONLY USED INTERFACES AND PATTERNS



// A class to demonstrate the use of Employee.

public class Recipe13_02

{

public static void Main()

{

// Create the original team.

Team team = new Team();

team.AddMember(new Employee("Frank", "Developer", 34));

team.AddMember(new Employee("Kathy", "Tester", 78));

team.AddMember(new Employee("Chris", "Support", 18));

// Clone the original team.

Team clone = (Team)team.Clone();

// Display the original team.

Console.WriteLine("Original Team:");

Console.WriteLine(team);

// Display the cloned team.

Console.WriteLine("Clone Team:");

Console.WriteLine(clone);

// Make change.

Console.WriteLine("*** Make a change to original team ***");

Console.WriteLine(Environment.NewLine);

team.TeamMembers[0].Name = "Luke";

team.TeamMembers[0].Title = "Manager";

team.TeamMembers[0].Age = 44;

// Display the original team.

Console.WriteLine("Original Team:");

Console.WriteLine(team);

// Display the cloned team.

Console.WriteLine("Clone Team:");

Console.WriteLine(clone);

// Wait to continue.

Console.WriteLine(Environment.NewLine);

Console.WriteLine("Main method complete. Press Enter");

Console.ReadLine();

}

}

}



630



www.it-ebooks.info



CHAPTER 13 ■ COMMONLY USED INTERFACES AND PATTERNS



13-3. Implement a Comparable Type

Problem

You need to provide a mechanism that allows you to compare custom types, enabling you to easily sort

collections containing instances of those types.



Solution

To provide a standard comparison mechanism for a type, implement the generic System.IComparable

interface. To support the comparison of a type based on more than one characteristic, create separate

types that implement the generic System.Collections.Generic.IComparer interface.



How It Works

If you need to sort your type into only a single order, such as ascending ID number, or alphabetically

based on surname, you should implement the IComparable interface. IComparable defines a single

method named CompareTo, shown here:

int CompareTo(T other);

The value returned by CompareTo should be calculated as follows:





If the current object is less than other, return less than zero (for example,-1).







If the current object has the same value as other, return zero.







If the current object is greater than other, return greater than zero (for example, 1).



What these comparisons mean depends on the type implementing the IComparable interface. For

example, if you were sorting people based on their surname, you would do a String comparison on this

field. However, if you wanted to sort by birthday, you would need to perform a comparison of the

corresponding System.DateTime fields.

To support a variety of sort orders for a particular type, you must implement separate helper types

that implement the IComparer interface, which defines the Compare method shown here:

int Compare(T x, T y);

These helper types must encapsulate the necessary logic to compare two objects and return a value

based on the following logic:





If x is less than y, return less than zero (for example, -1).







If x has the same value as y, return zero.







If x is greater than y, return greater than zero (for example, 1).



631



www.it-ebooks.info



CHAPTER 13 ■ COMMONLY USED INTERFACES AND PATTERNS



The Code

The Newspaper class listed here demonstrates the implementation of both the IComparable and IComparer

interfaces. The Newspaper.CompareTo method performs a case-insensitive comparison of two Newspaper

objects based on their name fields. A private nested class named AscendingCirculationComparer

implements IComparer and compares two Newspaper objects based on their circulation fields. An

AscendingCirculationComparer object is obtained using the static Newspaper.CirculationSorter

property.

The Main method shown here demonstrates the comparison and sorting capabilities provided by

implementing the IComparable and IComparer interfaces. The method creates a System.Collections.

ArrayList collection containing five Newspaper objects. Main then sorts the ArrayList twice using the

ArrayList.Sort method. The first Sort operation uses the default Newspaper comparison mechanism

provided by the IComparable.CompareTo method. The second Sort operation uses an

AscendingCirculationComparer object to perform comparisons through its implementation

of the IComparer.Compare method.

using System;

using System.Collections.Generic;

namespace Apress.VisualCSharpRecipes.Chapter13

{

public class Newspaper : IComparable

{

private string name;

private int circulation;

private class AscendingCirculationComparer : IComparer

{

// Implementation of IComparer.Compare. The generic definition of

// IComparer allows us to ensure both arguments are Newspaper

// objects.

public int Compare(Newspaper x, Newspaper y)

{

// Handle logic for null reference as dictated by the

// IComparer interface. Null is considered less than

// any other value.

if (x == null && y == null) return 0;

else if (x == null) return -1;

else if (y == null) return 1;

// Short-circuit condition where x and y are references

// to the same object.

if (x == y) return 0;

// Compare the

//

return

//

return

//

return



circulation figures. IComparer dictates that:

less than zero if x < y

zero if x = y

greater than zero if x > y



632



www.it-ebooks.info



Tài liệu bạn tìm kiếm đã sẵn sàng tải về

13-2. Implement a Cloneable Type

Tải bản đầy đủ ngay(0 tr)

×