Monday, April 20, 2009

Comparing your objects: IComparable & IComparer Interfaces.

Have you ever try to make your objects “Sortables”? Some WinForms controls have sort functionality when you bind them to a DataTable or other DataSources; one of those is the DataGridView. However, when you bind them to a list of your own business objects, sorting it’s not completely automatic.

That's when Comparing Objects enters to scene.

To sort a list of values (or objects), the control needs to know how to compare them. The interfaces IComparable and IComparer are provided for this goal.


When implementing the IComparable interface, we are saying that a specific instance of the class can be compared to another one.

Let’s say we have an Employee class:

class Employee : IComparable

{

private String employeeId;

private String firstName;

private String lastName;
private Double salary;

}

(You may use properties of the class. For simplicity we’ll use fields)

We can implement the inteface IComparable; the IDE will help us to generate the functions of the interface by putting the cursor in the name of the inteface and clicking “Implement Interface”.


I like the explicitly way, but it’s a matter of taste (or company standards)

class Employee : IComparable<Employee>

{

private String employeeId;

private String firstName;

private String lastName;

private Double salary;


#region IComparable Members

int IComparable.CompareTo(Object obj)
{


}

#endregion

}


Now you just have to decide how you are comparing the Employees. When you say EmployeeA > EmployeeB, how the employees should be compared? Employee A is greatest than B because of his salary? Let’s say that the default comparison will be the employee ID in order to don’t hurt anyone’s feelings.

int IComparable.CompareTo(Object obj)
{

this.employeeId.CompareTo(((Employee)obj).employeeId);

}


We are comparing the employee ID of our object against the employee ID of the object received as parameter in the function.

Here we have two things to point out:
A) We are using the CompareTo method of the String Class; after all, employee ID is a String; and
B) We have to cast the object received as an Employee.

Point A is not an issue. Usually, we are going to have an ID or some identifier property in our class to compare against it. This ID will often be a String, int, DateTime or some sort of base type. The good news is that all of these types implements IComparable, so they have the CompareTo method. But if you need to use a non-IComparable property or need a whole specific algorithm to compare two objects, you can change the CompareTo method to fit your needs. The only thing you have to remember is to return an integer representing if the instance is less, equal or greater than the parameter.

Instance < Object returns a value less than zero
Instance == Object returns zero
Instance > Object returns a value grater than zero

Now, the real problem comes with point B. What if we received an object that is not an Employee? We would have an Exception in that case. So, in order to fix that, we’ll use the Generic version of the IComparable interface that allows only objects of the specified typed.


class Employee : IComparable<Employee>

{

private String employeeId;

private String firstName;

private String lastName;

private Double salary;


#region IComparable Members

int IComparable<Employee>.CompareTo(Employee other)
{

this.employeeId.CompareTo(other.employeeId);

}

#endregion

}


Now we are sure we are comparing two Employees.

This is it for IComparable Inteface. In a (near) future post we'll see the IComparer Interface, the differences between these two and an example of using them.

No comments:

Post a Comment