AddThis Social Bookmark Button

Print

C# Generics: Collection Interfaces
Pages: 1, 2, 3, 4, 5, 6, 7

Implementing IComparer

When you call Sort( ) on the List, the default implementation of IComparer is called, which uses QuickSort to call the IComparable implementation of CompareTo() on each element in the List.

You are free to create your own implementation of IComparer, which you might want to do if you need control over how the sort ordering is defined. In the next example, you will add a second field to Employee, yearsOfSvc. You want to be able to sort the Employee objects in the List on either field, empID or yearsOfSvc.

To accomplish this, create a custom implementation of IComparer, which you pass to the Sort() method of the List. This IComparer class, EmployeeComparer, knows about Employee objects and knows how to sort them.

EmployeeComparer has the WhichComparison property, of type Employee. EmployeeComparer.ComparisonType:

public Employee.EmployeeComparer.ComparisonType
   WhichComparison 
{
   get{return whichComparison;}
   set{whichComparison = value;}
}

ComparisonType is an enumeration with two values, empID or yearsOfSvc (indicating that you want to sort by employee ID or years of service, respectively):

public enum ComparisonType
{
   EmpID,
   YearsOfService
};

Before invoking Sort( ), create an instance of EmployeeComparer and set its ComparisionType property:

Employee.EmployeeComparer c = Employee.GetComparer(); 
c.WhichComparison=Employee.EmployeeComparer.ComparisonType.EmpID;
empArray.Sort(c);

When you invoke Sort( ), the List calls the Compare method on the EmployeeComparer, which in turn delegates the comparison to the Employee.CompareTo() method, passing in its WhichComparison property:

public int Compare( Employee lhs, Employee rhs )
{
    return lhs.CompareTo( rhs, WhichComparison );
}

The Employee object must implement a custom version of CompareTo( ), which takes the comparison and compares the objects accordingly:

public int CompareTo(
    Employee rhs, 
    Employee.EmployeeComparer.ComparisonType which)
{
   switch (which)
   {
      case Employee.EmployeeComparer.ComparisonType.EmpID:
         return this.empID.CompareTo(rhs.empID);
      case Employee.EmployeeComparer.ComparisonType.Yrs:
         return this.yearsOfSvc.CompareTo(rhs.yearsOfSvc);
   }
   return 0;
}

The complete source for this example is shown in Example 9-15. The integer array has been removed to simplify the example, and the output of the employee's ToString( ) method has been enhanced to enable you to see the effects of the sort.

Example 9-15. Sorting an array by employees' IDs and years of service
#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace IComparer
{
   public class Employee : IComparable<Employee>

   {
      private int empID;

      private int yearsOfSvc = 1;

      public Employee( int empID )
      {
         this.empID = empID;
      }

      public Employee( int empID, int yearsOfSvc )
      {
         this.empID = empID;
         this.yearsOfSvc = yearsOfSvc;
      }

      public override string ToString( )
      {
         return "ID: " + empID.ToString( ) +
         ". Years of Svc: " + yearsOfSvc.ToString( );
      }

      public bool Equals( Employee other )
      {
         if ( this.empID == other.empID )
         {
            return true;
         }
         else
         {
            return false;
         }
      }

      // static method to get a Comparer object
      public static EmployeeComparer GetComparer( )
      {
         return new Employee.EmployeeComparer( );
      }

      // Comparer delegates back to Employee
      // Employee uses the integer's default
      // CompareTo method
      public int CompareTo( Employee rhs )
      {
         return this.empID.CompareTo( rhs.empID );
      }

      // Special implementation to be called by custom comparer
      public int CompareTo(
         Employee rhs,
         Employee.EmployeeComparer.ComparisonType which )
      {
         switch ( which )
         {
            case Employee.EmployeeComparer.ComparisonType.EmpID:
               return this.empID.CompareTo( rhs.empID );
            case Employee.EmployeeComparer.ComparisonType.Yrs:
               return this.yearsOfSvc.CompareTo( rhs.yearsOfSvc );
         }
         return 0;

      }

      // nested class which implements IComparer
      public class EmployeeComparer : IComparer<Employee>

      {
         
         // private state variable
         private Employee.EmployeeComparer.ComparisonType
            whichComparison;

         // enumeration of comparison types
         public enum ComparisonType
         {
            EmpID,
            Yrs
         };

         public  bool Equals( Employee lhs, Employee rhs )
         {
            return this.Compare( lhs, rhs ) == 0;
         }

         public  int GetHashCode(Employee e)
         {
            return e.GetHashCode( );
         }

         // Tell the Employee objects to compare themselves
         public int Compare( Employee lhs, Employee rhs )
         {
             return lhs.CompareTo( rhs, WhichComparison );
         }

         public Employee.EmployeeComparer.ComparisonType

            WhichComparison 
         {
            get{return whichComparison;}
            set{whichComparison = value;}
         }
      }
   }
   public class Tester
   {
      static void Main( )
      {
         List<Employee> empArray = new List<Employee>( );

         // generate random numbers for 
         // both the integers and the
         // employee id's
         Random r = new Random( );

         // populate the array
         for ( int i = 0; i < 5; i++ )
         {
            // add a random employee id

            empArray.Add(
               new Employee(
                  r.Next( 10 ) + 100, r.Next( 20 )
               )
            );
         }

         // display all the contents of the Employee array
         for ( int i = 0; i < empArray.Count; i++ )
         {
            Console.Write( "\n{0} ", empArray[i].ToString( ) );
         }
         Console.WriteLine( "\n" );

         // sort and display the employee array
         Employee.EmployeeComparer c = Employee.GetComparer( );
         c.WhichComparison = 
            Employee.EmployeeComparer.ComparisonType.EmpID;
         empArray.Sort( c );

         // display all the contents of the Employee array
         for ( int i = 0; i < empArray.Count; i++ )
         {
            Console.Write( "\n{0} ", empArray[i].ToString( ) );
         }
         Console.WriteLine( "\n" );

         c.WhichComparison = Employee.EmployeeComparer.ComparisonType.Yrs;
         empArray.Sort( c );

         for ( int i = 0; i < empArray.Count; i++ )
         {
            Console.Write( "\n{0} ", empArray[i].ToString( ) );
         }
         Console.WriteLine( "\n" );
      }
   }
}

Output:
ID: 103. Years of Svc: 11
ID: 108. Years of Svc: 15
ID: 107. Years of Svc: 14
ID: 108. Years of Svc: 5
ID: 102. Years of Svc: 0

ID: 102. Years of Svc: 0
ID: 103. Years of Svc: 11
ID: 107. Years of Svc: 14
ID: 108. Years of Svc: 15
ID: 108. Years of Svc: 5

ID: 102. Years of Svc: 0
ID: 108. Years of Svc: 5
ID: 103. Years of Svc: 11
ID: 107. Years of Svc: 14
ID: 108. Years of Svc: 15

The first block of output shows the Employee objects as they are added to the List. The employee ID values and the years of service are in random order. The second block shows the results of sorting by the employee ID, and the third block shows the results of sorting by years of service.

TIP: If you are creating your own collection, as in Example 9-11, and wish to implement IComparer, you may need to ensure that all the types placed in the list implement IComparer (so that they may be sorted), by using constraints as described earlier.

Pages: 1, 2, 3, 4, 5, 6, 7

Next Pagearrow