在 C# 開發中,當我們需要對自訂物件集合進行排序時,實作 IComparable<T> 介面是最基礎的做法。但實務上常遇到需依據不同欄位排序的情況,例如員工資料既要按工號排序,也要按工齡排序。本文將透過完整範例,示範如何結合泛型與巢狀類別設計,實現靈活的多欄位排序機制。
我們定義一個 Employee 類別,包含員工編號(empID)與工齡(yearsOfSvc)兩個屬性。關鍵在於實作 IComparable<Employee> 介面,並建立巢狀類別 EmployeeComparer 實作 IComparer<Employee>,透過列舉型別 ComparisonType 控制排序依據。
public class Employee : IComparable<Employee>
{
private int empID;
private int yearsOfSvc = 1;
public Employee(int empID) { this.empID = empID; }
public Employee(int empID, int yearsOfSvc)
{
this.empID = empID;
this.yearsOfSvc = yearsOfSvc;
}
public override string ToString()
{
return $"ID:{empID}. Years of Svc:{yearsOfSvc}";
}
// 預設比較:依員工編號
public int CompareTo(Employee rhs)
{
return this.empID.CompareTo(rhs.empID);
}
// 多欄位比較核心邏輯
public int CompareTo(Employee rhs, EmployeeComparer.ComparisonType which)
{
switch (which)
{
case EmployeeComparer.ComparisonType.EmpID:
return this.empID.CompareTo(rhs.empID);
case EmployeeComparer.ComparisonType.Yrs:
return this.yearsOfSvc.CompareTo(rhs.yearsOfSvc);
}
return 0;
}
// 取得比較器實例
public static EmployeeComparer GetComparer()
{
return new EmployeeComparer();
}
// 巢狀比較器類別
public class EmployeeComparer : IComparer<Employee>
{
private ComparisonType whichComparison;
public enum ComparisonType { EmpID, Yrs }
public ComparisonType WhichComparison
{
get { return whichComparison; }
set { whichComparison = value; }
}
public int Compare(Employee lhs, Employee rhs)
{
return lhs.CompareTo(rhs, WhichComparison);
}
// 介面必要實作(簡化版)
public bool Equals(Employee lhs, Employee rhs) => Compare(lhs, rhs) == 0;
public int GetHashCode(Employee e) => e.GetHashCode();
}
}
在 Tester 類別中,我們建立員工清單並展示三種狀態:原始順序、依工號排序、依工齡排序。
public class Tester
{
static void Main()
{
List<Employee> empArray = new List<Employee>();
Random r = new Random();
// 產生 5 筆隨機測試資料
for (int i = 0; i < 5; i++)
{
empArray.Add(new Employee(r.Next(10) + 100, r.Next(20)));
}
Console.WriteLine("=== 原始資料 ===");
empArray.ForEach(e => Console.WriteLine(e));
// 取得比較器並設定排序模式
EmployeeComparer comparer = Employee.GetComparer();
// 依員工編號排序
comparer.WhichComparison = EmployeeComparer.ComparisonType.EmpID;
empArray.Sort(comparer);
Console.WriteLine("\n=== 依員工編號排序 ===");
empArray.ForEach(e => Console.WriteLine(e));
// 依工齡排序
comparer.WhichComparison = EmployeeComparer.ComparisonType.Yrs;
empArray.Sort(comparer);
Console.WriteLine("\n=== 依工齡排序 ===");
empArray.ForEach(e => Console.WriteLine(e));
}
}
=== 原始資料 ===
ID:103. Years of Svc:11
ID:108. Years of Svc:15
ID:107. Years of Svc:14
ID:108. Years of Svc:5
ID:102. Years of Svc:0
=== 依員工編號排序 ===
ID:102. Years of Svc:0
ID:103. Years of Svc:11
ID:107. Years of Svc:14
ID:108. Years of Svc:15
ID:108. Years of Svc:5
=== 依工齡排序 ===
ID:102. Years of Svc:0
ID:108. Years of Svc:5
ID:103. Years of Svc:11
ID:107. Years of Svc:14
ID:108. Years of Svc:15
結果清晰顯示:原始資料為隨機順序;第一次排序依 empID 遞增;第二次排序依 yearsOfSvc 遞增。注意員工編號 108 出現兩次,排序演算法保持穩定性。
Employee.CompareTo(Employee, ComparisonType) 方法中,避免分散在各處。EmployeeComparer 透過 WhichComparison 屬性動態切換排序策略,符合開放封閉原則。IComparer<Employee> 避免 boxing/unboxing,效能優於非泛型版本。若自行實作集合類別並需支援排序,建議加入泛型約束 where T : IComparable<T>,確保放入集合的型別皆可比較。此範例展示的模式廣泛適用於任何需要多欄位、可切換排序邏輯的業務場景。