Monday, May 4, 2009

Comparing your objects: IComparable & IComparer Interfaces (Part II)

In a previous post we saw that implementing the IComparable Interface allows an instance of our class to be compared against another object of the same type. We can choose a property of our class as the default criteria of comparison.

But what if we want to have multiple criteria for comparing objects? With IComparable we can have only one compareTo method, but IComparer helps us to compare objects using different criteria.

A big difference between these two Interfaces is that when our class implements IComparable it adds a compareTo method to our Class, but IComparer must be implemented in a different class. So we are going to have a Comparer class to help us compare our objects.

To illustrate the usage of the IComparer Interface, let's use the example of the previous post. This is our Employee class that implements IComparable and uses the EmployeeId property as the comparison criteria:

public class Employee : IComparable<Employee>

{

//Properties

private String employeeId;

public String EmployeeId

{

get { return employeeId; }

set { employeeId = value; }

}

private String firstName;

public String FirstName

{

get { return firstName; }

set { firstName = value; }

}

private String lastName;

public String LastName

{

get { return lastName; }

set { lastName = value; }

}

private Double salary;

public Double Salary

{

get { return salary; }

set { salary = value; }

}

//Constructor

public Employee(String id, String firstName, String lastName, Double salary)

{

this.employeeId = id;

this.firstName = firstName;

this.lastName = lastName;

this.salary = salary;

}

//IComparable

#region IComparable Members

int IComparable<Employee>.CompareTo(Employee other)

{

return this.EmployeeId.CompareTo(other.EmployeeId);

}

#endregion

}



Now, we'll create a Comparer class to compare Employee objects using the FirstName property.

public class EmployeeComparerFirstName : IComparer<Employee>

{

public int Compare(Employee x, Employee y)

{

return x.FirstName.CompareTo(y.FirstName);

}

}


We are using the Generic version of the Interface but you can use the other one (see in Part I how to do it).

And this way, you can create a Comparer class for each property of your class, or even code an algorithm inside the Compare method. For now, let's create another Comparer for the salary:

public class EmployeeComparerSalary : IComparer<Employee>

{

public int Compare(Employee x, Employee y)

{

return x.Salary.CompareTo(y.Salary);

}

}


So, comparing your objects is simple. But what is the purpose? Here is an example of using IComparer and IComparable to sort a List of Employees:

class Program

{

static void Main(string[] args)

{

List<Employee> employees = new List<Employee>();

//add employees to the list

employees.Add(new Employee("1", "Elijah", "Baley", 5000.50));

employees.Add(new Employee("2", "Daneel", "Olivaw", 3650.80));

employees.Add(new Employee("3", "Hari", "Seldon", 8450.25));

printList(employees);

//sort by first name

employees.Sort(new EmployeeComparerFirstName());

printList(employees);

//sort by salary

employees.Sort(new EmployeeComparerSalary());

printList(employees);

//sort by the default field: employee ID

employees.Sort();

printList(employees);

Console.ReadKey();

}

//method to print the list

static void printList(List<Employee> list)

{

foreach (Employee employee in list)

{

Console.WriteLine(employee.EmployeeId + " " + employee.FirstName + " " +

employee.LastName + " " + employee.Salary.ToString());

}

Console.WriteLine("------------------------------------");

}

}



It's a console application that creates a List of Employees and sorts it using our comparers. The Sort method of List of Employees receives either a Comparer or nothing at all (in an overloaded version of the method). If you don't provide a Comparer, the Sort method will use the compareTo method of the class.

Run it and see it for yourself. Play with it. You can notice that if Employees does Not implements IComparable and you don't provide a Comparer when sorting the list, you will get an exception because the Sort method doesn't know how to compare the employees.

Another point is that if your are using a Generic List (like in the example) you have to use the Generic versions of IComparer and IComparable (to know how to use the non-generic versions go to the previous post).

A really good usage of Comparers is when you bind your List of objects to a Control and want to use the Sort functionality of the Control. In the next post we'll see how to sort a DataGridView in WinForms when it is bound to a List of business objects.

No comments:

Post a Comment