16.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 System.IComparable interface. To support the comparison of a type based on more than one characteristic, create separate types that implement the System.Collections.IComparer interface.

Discussion

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

The object ( obj ) passed to the method must be an object of the same type as that being called, or CompareTo must throw a System.ArgumentException . The value returned by CompareTo is calculated as follows :

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

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

  • If the current object is greater than obj , 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. However, if you wanted to sort by birthday, you would need to perform a comparison of System.DateTime objects.

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(object x, object 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).

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.

 using System; using System.Collections; public class Newspaper : IComparable {     private string name;     private int circulation;     private class AscendingCirculationComparer : IComparer {                  int IComparer.Compare(object x, object 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;                          // Ensure both x and y are Newspaper instances             Newspaper newspaperX = x as Newspaper;             if (newspaperX == null) {                                  throw new ArgumentException("Invalid object type", "x");             }                          Newspaper newspaperY = y as Newspaper;             if (newspaperY == null) {                                  throw new ArgumentException("Invalid object type", "y");             }                          // Compare the circulation figures. IComparer dictates that:             //      return less than zero if x < y             //      return zero if x = y             //      return greater than zero if x > y             // This logic is easily implemented using integer arithmetic.             return newspaperX.circulation - newspaperY.circulation;         }     }                  public Newspaper(string name, int circulation) {              this.name = name;         this.circulation = circulation;     }     // Declare a read-only property that returns an instance of the      // AscendingCirculationComparer.     public static IComparer CirculationSorter{         get { return new AscendingCirculationComparer(); }     }          public override string ToString() {                  return string.Format("{0}: Circulation = {1}", name, circulation);     }          // The CompareTo method compares two Newspaper objects based on a      // case insensitive comparison of the Newspaper names.     public int CompareTo(object obj) {                          // IComparable dictates that an object is always considered greater         // than null.         if (obj == null) return 1;         // Short circuit the case where the other object is a reference         // to this object.         if (obj == this) return 0;                           // Try to cast the other object to a Newspaper instance.         Newspaper other = obj as Newspaper;                  // If "other" is null, it must not be a Newspaper instance.         // IComparable dictates CompareTo must throw the exception         // System.ArgumentException in this situation.         if (other == null) {                          throw new ArgumentException("Invalid object type", "obj");                      } else {                          // Calculate return value by performing a case-insensitive              // comparison of the Newspaper names.                                      // Because the Newspaper name is a string, the easiest approach             // is to rely on the comparison capabilities of the String              // class, which perform culture-sensitive string comparisons.             return string.Compare(this.name, other.name, true);         }     } } 

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.

 public static void Main() {              ArrayList newspapers = new ArrayList();              newspapers.Add(new Newspaper("The Echo", 125780));     newspapers.Add(new Newspaper("The Times", 55230));     newspapers.Add(new Newspaper("The Gazette", 235950));     newspapers.Add(new Newspaper("The Sun", 88760));     newspapers.Add(new Newspaper("The Herald", 5670));              Console.WriteLine("Unsorted newspaper list:");     foreach (Newspaper n in newspapers) {          Console.WriteLine(n);     }              Console.WriteLine(Environment.NewLine);     Console.WriteLine("Newspaper list sorted by name (default order):");     newspapers.Sort();     foreach (Newspaper n in newspapers) {          Console.WriteLine(n);     }     Console.WriteLine(Environment.NewLine);     Console.WriteLine("Newspaper list sorted by circulation:");     newspapers.Sort(Newspaper.CirculationSorter);     foreach (Newspaper n in newspapers) {         Console.WriteLine(n);     } } 

Running the Main method will produce the results shown here.

 Unsorted newspaper list: The Echo: Circulation = 125780 The Times: Circulation = 55230 The Gazette: Circulation = 235950 The Sun: Circulation = 88760 The Herald: Circulation = 5670 Newspaper list sorted by name (default order): The Echo: Circulation = 125780 The Gazette: Circulation = 235950 The Herald: Circulation = 5670 The Sun: Circulation = 88760 The Times: Circulation = 55230 Newspaper list sorted by circulation: The Herald: Circulation = 5670 The Times: Circulation = 55230 The Sun: Circulation = 88760 The Echo: Circulation = 125780 The Gazette: Circulation = 235950 



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