.NET框架定義了兩個接口來描述類型的排序關係:IComparable和IComparer,其中IComparable接口定義了類型的自然排序方式,IComparer則爲類型提供了另外的排序方式。
我們來看下面的代碼。
public struct Employee : IComparable
{
private string m_strName;
public string Name
{
get { return m_strName; }
set { m_strName = value; }
}
private int m_nAge;
public int Age
{
get { return m_nAge; }
set { m_nAge = value; }
}
public Employee(string name, int age)
{
m_strName = name;
m_nAge = age;
}
public int CompareTo(object obj)
{
if (!(obj is Employee))
{
throw new ArgumentException("Type Error!");
}
Employee emp = (Employee)obj;
return this.Name.CompareTo(emp.Name);
}
}
上面的代碼定義了一個結構體,實現了IComparable接口,接口中方法的聲明方式是int CompareTo(object obj),該方法接收一個Object對象,和當前對象進行比較,如果當前對象大於比較對象,則返回結果大於0;如果當前對象小於比較對象則返回結果爲-1;如果當前對象和比較對象相同,則返回0。
下面是測試代碼。
private static void Test()
{
Employee emp1 = new Employee("Wing", 24);
Employee emp2 = new Employee("UnKnown", 25);
int nResult = emp1.CompareTo(emp2);
if (nResult > 0)
{
Console.WriteLine("emp1 is larger than emp2.");
}
else if (nResult < 0)
{
Console.WriteLine("emp1 is smaller than emp2.");
}
}
上述代碼執行後,會在命令行中輸出如下內容:emp1 is larger than emp2.
我們再來看上面的代碼,其中Employee類型是一個結構體,屬於值類型,但是IComparable接口的方法接收參數的類型是Object,這樣我們在CompareTo()方法中必須進行裝箱和拆箱的操作,才能完成比較操作。這樣做對性能的影響比較大,特別是需要比較的數據非常多的時候,例如一個大數據量的集合。爲了解決我們需要對上述Employee類型的代碼進行修改,修改後的代碼如下。
public struct Employee : IComparable
{
private string m_strName;
public string Name
{
get { return m_strName; }
set { m_strName = value; }
}
private int m_nAge;
public int Age
{
get { return m_nAge; }
set { m_nAge = value; }
}
public Employee(string name, int age)
{
m_strName = name;
m_nAge = age;
}
int IComparable.CompareTo(object obj)
{
if (!(obj is Employee))
{
throw new ArgumentException("Type Error!");
}
Employee emp = (Employee)obj;
return CompareTo(emp);
}
public int CompareTo(Employee emp)
{
return this.Name.CompareTo(emp.Name);
}
}
上述代碼中,我們顯示實現了IComparable接口,但是將訪問限制符改爲默認設置,即internal,同時添加了一個重載類型的CompareTo()方法, 接收一個Employee類型對象作爲參數。這樣,在執行Test()方法時,就會調用重載後的方法,繞過了裝箱和拆箱。
注意:在實現了IComparable接口後,不需要重寫Equals()方法,因爲大部分情況下,Equals()方法是針對對象的引用進行比較,而IComparable接口是針對對象中的內容進行比較;因此可以出現以下的情況:兩個對象調用CompareTo()方法後返回爲0,但是調用Equals()方法後返回false。
另外,如果我們實現了IComparable接口,一般情況下需要對操作符進行重載,這些操作符包括:<、>、<=、>=和!=。
如果我們需要自己定製比較規則,那麼我們可以通過實現IComparer接口來實現這個目標。
來看下面的代碼。
public class AgeComparer : IComparer<Employee>
{
public int Compare(Employee x, Employee y)
{
return x.Age.CompareTo(y.Age);
}
}
//Test Method
private static void Test()
{
List<Employee> listEmp = new List<Employee>();
listEmp.Add(new Employee("Wing", 24));
listEmp.Add(new Employee("UnKnown", 25));
listEmp.Sort();
Console.WriteLine("ouput info with default sort:");
foreach (Employee emp in listEmp)
{
Console.WriteLine(emp.Name);
}
Console.WriteLine("output info with specific sort:");
AgeComparer comparer = new AgeComparer();
listEmp.Sort(comparer);
foreach (Employee emp in listEmp)
{
Console.WriteLine(emp.Name);
}
}
上面的代碼中,首先定義了一個實現了IComparer接口的類型,該類型的Compare()方法中,以Employee的Age作爲比較的依據。然後定義了一個測試方法,定義了一個元素類型是Employee類型的List,然後以兩種方式對List進行排序,並輸出排序後的結果。
上面Test()方法的執行結果如下所示。
綜上,IComparable接口和IComparer接口爲類型實現排序關係提供了兩種標準的機制,IComparable接口應該用於爲類型實現最自然的排序關係,而ICpmparer接口則用於定製排序的方式。