你的型別應有順序關係,便於集合中描述儲存與排序。.NET 框架提供兩介面描述物件順序關係:IComparable 與 IComparer。IComparable 為你的類別定義自然順序,實作 IComparer 的類別可描述其他可選順序。實作介面時,定義並實作自己關係運算子(<、>、<=、>=),避免執行期預設比較關係低效問題。本原則討論如何實作順序關係,讓 .NET 核心透過你定義介面對型別排序,使用者在操作上得更好效率。
IComparable 只有一方法 CompareTo(),沿用 C 函式庫 strcmp 原則:目前物件小於目標回傳 < 0;相等回傳 0;大於回傳 > 0。IComparable 以 System.Object 為參數,使用時必須檢查執行期物件型別。每次比較必須重新解釋參數型別:
public struct Customer : IComparable
{
private readonly string _name;
public Customer(string name) { _name = name; }
public int CompareTo(object right)
{
if (!(right is Customer)) throw new ArgumentException("Argument not a customer", "right");
Customer rightCustomer = (Customer)right;
return _name.CompareTo(rightCustomer._name);
}
}
實作比較與 IComparable 一致性有很多不喜歡處:檢查參數執行期型別;不正確程式碼可用任何型別調用 CompareTo;正確參數仍必須裝箱拆箱才能比較。每次比較額外開銷。排序時平均比較次數 N x log(N),每次 3 次裝箱拆箱。1000 元素陣列約 20000 次裝箱拆箱(N x log(n) 約 7000 次比較 x 3)。必須自己找可選比較方法。無法改變 IComparable.CompareTo() 定義,但不意味著被迫讓使用者在弱型別實作忍受效能損失。可重載 CompareTo() 只對 Customer 物件操作:
public struct Customer : IComparable
{
private string _name;
public Customer(string name) { _name = name; }
int IComparable.CompareTo(object right)
{
if (!(right is Customer)) throw new ArgumentException("Argument not a customer", "right");
Customer rightCustomer = (Customer)right;
return CompareTo(rightCustomer);
}
public int CompareTo(Customer right) { return _name.CompareTo(right._name); }
}
現在 IComparable.CompareTo() 成隱式介面實作,只能透過 IComparable 介面參考調用。使用者只能用型別安全調用,不安全比較無法存取。下列無意錯誤無法通過編譯:
Customer c1; Employee e1;
if (c1.CompareTo(e1) > 0) Console.WriteLine("Customer one is greater");