Building C# Comparable Objects: IComparable versus IComparer

Working with object-oriented languages (like C#) is all about manipulating objects. We create objects, change objects properties, save objects states, and sort objects. The part we are concentrating on in this article is sorting objects.

Contributed by
Rating: 4 stars4 stars4 stars4 stars4 stars / 9
August 04, 2008
Rate this Article:
MEH MEH++


SEARCH ASP FREE
TOOLS YOU CAN USE

advertisement

Objects that are part of the language (such as strings) can be sorted without any additional work on our end. But when it comes to classes that we create (Book, Employee, Car…), we need to give hints to the runtime on how objects of these classes can be sorted (what does it mean that book1 comes before book2? Are they sorted by title? By ISBN?...). The hints that we give to the runtime are the interfaces. We implement interfaces such as IComparable and IComparer so the runtime can tell how to compare and sort objects.

Interface definition: An interface is a named collection of abstract methods. Abstract means that the methods are not implemented. It is left up to the user of the interface to implement these methods the way he/she sees fit. When you create an interface you are creating a contract, which means that any code implementing the interface needs to implement all the methods in the interface (you simply cannot pick and choose which methods to implement).

To create an interface you use the keyword interface followed by the name of the interface. Interfaces do not specify a base class, and all interface members are implicitly public. Here is an interface declaration:

interface   IDraw

{

void Draw();

}

By convention, interface names are prefixed by the letter ‘I’. Based on this declaration, any class that needs to implement the interface IDraw must provide implementation for the method Draw(). Let’s create a class named Circle that implements the IDraw Interface:

class   Circle : IDraw

{

public   override   void Draw()

{

Console .WriteLine( "Drawing a circle." );

}

}

Sorting Objects

In our first attempt to sort objects, we create an Employee class with name and id. In the Main() method we create an array of five employees. The System.Array class provides a Sort() method that sorts an array of objects. When we try to run the following code we end up with a runtime exception: “At least one object must implement IComparable.” In other words, the error is asking us what we mean by sorting employees.

class Program

{

static   void Main( string [] args)

{

Employee[] employees = new Employee[5];

employees[0] = new Employee( "Dan K." , 12456);

employees[1] = new Employee( "Jim L." , 99584);

employees[2] = new Employee( "Tony T." , 51472);

employees[3] = new Employee( "Jessica W." , 32788);

employees[4] = new Employee( "Leda B." , 44110);


//**Runtime exception: at least one object must

//**implement IComparable

Array .Sort(employees);

}

}


class Employee

{

private   string name;

public   string Name

{

get { return name; }

set { name = value ; }

}

private   int id;

public   int Id

{

get { return id; }

set { id = value ; }

}


public Employee( string a_name, int an_id)

{

name = a_name;

id = an_id;

}

}

It is clear then that we need to implement the IComparable interface to be able to sort the array of employees. The IComparable interface contains a single method called CompareTo:

  public   int CompareTo( object obj)

The method accepts an object as a parameter and returns an integer. Since object is the parent of all objects, passing an Employee or any other object will work. The return value could be positive, negative, or zero:

  • A return value of zero means that the two objects we are comparing are equal to each other.

  • A negative return value means that the first object comes before the second object.

  • A positive return value means that the first object comes after the second object.

The first thing we do in the CompareTo method is cast the object parameter into an Employee object. The we compare this object with the newly casted Employee object. In this case we decided to compare them by id. Here is how the code will look:

class Program

{

static   void Main( string [] args)

{

Employee[] employees = new Employee[5];

employees[0] = new Employee( "Dan K." , 12456);

employees[1] = new Employee( "Jim L." , 99584);

employees[2] = new Employee( "Tony T." , 51472);

employees[3] = new Employee( "Lee W." , 32788);

employees[4] = new Employee( "Leda B." , 44110);


Array .Sort(employees);

foreach (Employee e in employees)

Console .WriteLine(e);

}

}


class Employee : IComparable

{

private   string name;

public   string Name

{

get { return name; }

set { name = value ; }

}

private   int id;

public   int Id

{

get { return id; }

set { id = value ; }

}


public Employee( string a_name, int an_id)

{

name = a_name;

id = an_id;

}


public   override   string ToString()

{

return   string .Format( "Name: {0}ttId: {1}" ,

Name, Id);

}


#region IComparable Members


public   int CompareTo( object obj)

{

Employee temp = (Employee)obj;

if ( this .Id > temp.Id)

return 1;

if ( this .Id < temp.Id)

return -1;

else

return 0;

}


#endregion

}

In the Main() method, we sort the employees array in place and we use the foreach loop to print the list of employees. Try to run the code and notice in the output how the objects are sorted by id.

Quick Recap


If we need to sort objects for classes that we create, we need to implement the IComparable interface to tell the runtime how to sort the objects. In our employee example we sorted the list of employees by id. Can we sort the employees by name instead? Sure we can. Just change the CompareTo() method to return -1, 0, or 1 based on the name comparison, not the id.

Although this change works perfectly fine, it is short of being complete. When our sorting requirements change, the CompareTo() method changes. It will be ideal if we can keep the CompareTo() method as is, but implement other interfaces that satisfy different sorting conditions. This is where the IComparer interface comes to the rescue. The IComparer interface has a Compare() method that has the following signature:

  public   int Compare( object obj1, object obj2)

Notice that the method still returns an integer like the CompareTo() method in the IComparable interface. The difference, however, is in the parameters. This method takes two parameters instead of one: obj1 and obj2. These are the two objects that we are comparing. We first cast them to the appropriate type, then we compare them based on our needs.

Since the Compare() method takes two objects that we are comparing as parameters, we normally do not implement the IComparer interface in the same class where we implemented the IComparable interface. Instead, we create a separate class that implements the IComparer interface and pass a new instance of that class to the Array.Sort() method. This method is overloaded to accept an IComparer interface as a parameter:


public static void Sort(Array array, IComparer comparer)

Let’s first implement the method Compare()in a class called EmployeeComparer :


  class   EmployeeComparer : IComparer

{

public EmployeeComparer() { }


public   int Compare( object obj1, object obj2)

{

Employee e1 = ( Employee )obj1;

Employee e2 = ( Employee )obj2;

return   string .Compare(e1.Name, e2.Name);

}

 }

Notice how we cast the objects to Employee , then we call the Compare() method of the string class to do the actual comparison by passing the employees’ names as parameters. The only thing we have left now is to pass an instance of the EmployeeComparer class to the Sort method:


  Array .Sort(employees, new   EmployeeComparer ());


By doing so, the employees can now be sorted by name instead of id. We can even go a step further by defining a static property in the Employee class that returns a new EmployeeComparer casted as an IComparer :


public   static   IComparer SortByEmployeeName

{

get { return ( IComparer ) new EmployeeComparer(); }

}


Now the call in the Sort method is more natural:


Array .Sort(employees, Employee.SortByEmployeeName);

Full Program


Here is the full program:


class Program

{

static   void Main( string [] args)

{

Employee[] employees = new Employee[5];

employees[0] = new Employee( "Dan K." , 12456);

employees[1] = new Employee( "Jim L." , 99584);

employees[2] = new Employee( "Tony T." , 51472);

employees[3] = new Employee( "Lee W." , 32788);

employees[4] = new Employee( "Leda B." , 44110);


Array .Sort(employees);

PrintEmployees(employees);

Console .WriteLine();

Array .Sort(employees, Employee.SortByEmployeeName);

PrintEmployees(employees);

}


static   void PrintEmployees(Employee[] employees)

{

foreach (Employee e in employees)

Console .WriteLine(e);

}

}


class Employee : IComparable

{

private   string name;

public   string Name

{

get { return name; }

set { name = value ; }

}

private   int id;

public   int Id

{

get { return id; }

set { id = value ; }

}


public Employee( string a_name, int an_id)

{

name = a_name;

id = an_id;

}


public   override   string ToString()

{

return   string .Format( "Name: {0}ttId: {1}" ,

Name, Id);

}


public   static   IComparer SortByEmployeeName

{

get { return ( IComparer ) new EmployeeComparer(); }

}


#region IComparable Members


public   int CompareTo( object obj)

{

Employee temp = (Employee)obj;

if ( this .Id > temp.Id)

return 1;

if ( this .Id < temp.Id)

return -1;

else

return 0;

}


#endregion

}


class EmployeeComparer : IComparer

{

public EmployeeComparer() { }


#region IComparer Members


public   int Compare( object obj1, object obj2)

{

Employee e1 = (Employee)obj1;

Employee e2 = (Employee)obj2;

return   string .Compare(e1.Name, e2.Name);

}


#endregion

}

Conclusion

The System.Array.Sort() methods sorts objects of different types. While sorting predefined types (string…) is already implemented in the language, sorting objects for classes that we create requires implementing interfaces to tell the runtime how to sort these objects. We can implement the Comparable interface and override its CompareTo() method and/or the Comparer interface with its Compare() method. The Sort() method is overloaded to support both implementations.

blog comments powered by Disqus
C# ARTICLES

- Beginning C#
- ASP.NET RedirectPermanent Method using C# an...
- C Programming Language and UNIX Pioneer Pass...
- Using Facebook JavaScript SDK in ASP.NET wit...
- ASP.NET Export to Excel and Word using VB.NE...
- WAV and MP3 Streaming with ASP.Net and C#
- Game Programming using SDL: the File I/O API
- C# and Java Developer Jobs on the Rise
- The Future Evolution of C# and VB.NET
- C# If and Else-if Statements
- How To Use the C# String Replace Method
- 5 Ways to Parse XML in C#
- C# Meets Design Patterns
- Coding a CRC-Generating Algorithm in C
- Cyclic Redundancy Check

ASP Web Hosting ASP.Net Web Hosting Windows Web Hosting
ASP Free Forums 
 RSS  Tutorials RSS
 RSS  Forums RSS
 RSS  All Feeds
Site Map 
Request Media Kit
Write For Us Get Paid 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Privacy Policy 
Support 


© 2003-2012 by Developer Shed. All rights reserved. DS Cluster 11 - Follow our Sitemap
Most Popular Topics
All ASP.Net Tutorials