Sunday, July 18, 2010

Interface in C# - IComparable & IComparer



Sorting ArrayList of Custom Objects - IComparable, IComparer interfaces

A class can implement the IComparable interface to provide sorting on a particular field. The IComparable interface has a method called CompareTo which returns comparison result on a particular field. For example, if we have a class defined X with two data members a, and b (and the corresponding public properties A, B), then the IComparable.CompareTo method in class X may look as:

public int CompareTo(object obj)
{
return this.a.CompareTo((X)obj).a);
}

As you can see, the above method compares the a field of class X to decide the sorting order if sorted. As an example, if we created an ArrayList called Xlist of objects of type X, and executed
Xlist.SortO;
It will sort the X objects in XList according to a field.

But what if sometimes, we wanted to sort on the a field in X and sometimes on the b field in X?

For these type of needs, we can call the XList.Sort(IComparer ic) method and pass our own IComparer to indicate the sorting criteria. Passing the IComparer object is little tricky and requires creating a new class that implements the IComparer interface. The IComparer interface provides a method called Compare that can compare two objects. As an example, we can create a class called XCompare that implements the IComparer interface as shown below:

X Compare. cs -- -----¬
using System;
using System.Collections;

namespace Testlnheritance
{


Summary description for XCompare. III

public class XCompare : IComparer
{

string cmpField;
public string CmpField
{
get { return cmpField; }
set { cmpField = value; }
}

public XcompareO
{

}
public int Compare(object 01, object 02)
{
string cmpf = cmpField.ToUpperO;
int cmpNum = -1;
X xobj1 = null;
X xobj2 = null;
if (01 is X)
xobj1 = (X) 01 ;
if (02 is X)
xobj2 = (X) 02;
switch (cmpf)
{

case "A" :
return xobj1.CompareTo(xobj2,1);
break;
case "B" :
return xobj1.CompareTo(xobj2,2);
break;
}
return -1;

}
}
}
The above class has a property called CmpField that will decide the field in class X that provide the comparison. The Compare method in above class simply checks this field and based on its value, calls the overloaded CompareTo method in class X. Now we will need to overload the CompareTo method in class X such that it has an extra parameter fieldNum to decide which field in class X to do the comparison on.

The entire test code is shown below.

private void btnTestArray_Click(object sender, System.EventArgs e)
{
ArrayList a = new ArrayList();
a.Add(7);
a.Add(5) ;
a.Add(8);
// List before Sort MessageBox.Show(a[0].ToString()+":"+a[1].ToString()+":"+a[2].ToString());
a.Sort();
// List After Sort MessageBox.Show(a[0].ToString()+":"+a[1].ToString()+":"+a[2].ToString());
X x1 = new X();
x1.A = 2;
x1.B = 4;

X x2 = new X();
x2.A = 6;
x2.B =3;

X x3 = new X();
x3.A =3;
x3.B = 5;
ArrayList XList = new ArrayList();
XList.Add(x1);
XList.Add(x2) ;
XList.Add(x3);
XCompare xc = new XCompare();
xc.CmpField = "b";
XList.Sort(xc);
MessageBox.Show(XList[0].ToString());
MessageBox.Show(XList[1].ToString()); MessageBox.Show(XList[2].ToString());
}
------------------------------------x.cs ------------------------------------------------------------------¬
using System;
using System.Collections;

namespace XCompare
{
///
///
///

public class X: ICloneable, IComparable
{
protected int a;
public int A
{
get { return a; }
set { a = value; }
}
protected int b;
public int B
{
get { return b; }
set { b = value; }
}

public X()
{
//
// TODO: Add constructor logic here
//
}
public override string ToString()
{
return " a = " + a + " b = " + b;
}
public virtual object Clone()
{
X x1 = new X();
x1.a = this.a;
x1.b = this.b;
return x1 ;
}
public int CompareTo(object obj)
{
return this.a.CompareTo(((X)obj).a);
}

public int CompareTo(object obj, int field)
{

if (field == 1)
return this.a.CompareTo(((X)obj).a);
if (field == 2)
return b.CompareTo(((X)obj).b);
return -1;

}
}
}
// X Compare.cs----------------------------------------//¬
using System;
using System.Collections;

namespace XCompare
{
///
///
///

public class XCompare : IComparer
{
string cmpField;
public string CmpField
{
get { return cmpField; }
set { cmpField = value; }
}
public XCompare()
{
}
public int Compare(object o1, object o2)
{
string cmpf = cmpField.ToUpper();
X xobj1 = null;
X xobj2 = null;
if (o1 is X)
xobj1 = (X) o1 ;
if (o2 is X)
xobj2 = (X) o2;

switch (cmpf)
{
case "A" :
return xobj1.CompareTo(xobj2,1);

case "B" :
return xobj1.CompareTo(xobj2,2);

}
return -1;
}
}
}