本文通過示例介紹了C#中典型容器List.Sort()的自定義排序方法,進而引出了C#中自定義排序的核心接口及方法
項目地址:自定義Sort方法 - SouthBegonia's Github
List.Sort() 為我們提供了4種自定義排序方法,通過對這些方法改進我們可以輕松做到對多參數、多規則的復雜排序:
List<T>.Sort();
List<T>.Sort(IComparer<T> Comparer);
List<T>.Sort(int index, int count, IComparer<T> Comparer);
List<T>.Sort(Comparison<T> comparison);
項目背景
存在People類,包含Name、Age屬性,在客戶端中創建List
class People
{
public People(string name, int age) { Name = name; Age = age; }
public string Name { get; set; }
public int Age { get; set; }
}
// 客戶端
class Client
{
static void Main(string[] args)
{
List<People> peopleList = new List<People>();
peopleList.Add(new People("張三", 22));
peopleList.Add(new People("張三", 24));
peopleList.Add(new People("李四", 18));
peopleList.Add(new People("王五", 16));
peopleList.Add(new People("王五", 30));
}
}
List
.Sort()
該方法為系統默認的方法,單一參數時會默認進行升序排序。但遇到多參數(Name、Age)排序時,我們需要對該默認方法進行修改。
- 方法:People類繼承IComparable
,實現CompareTo()方法 IComparable<T>
:定義由值類型或類實現的通用比較方法,旨在創建特定於類型的比較方法以對實例進行排序。- 原理:自行實現的CompareTo()方法會在list.Sort()內部進行元素兩兩比較,最終實現排序
class People : IComparable<People>
{
public People(string name, int age) { Name = name;Age = age; }
public string Name { get; set; }
public int Age { get; set; }
// list.Sort()時會根據該CompareTo()進行自定義比較
public int CompareTo(People other)
{
if (this.Name != other.Name)
{
return this.Name.CompareTo(other.Name);
}
else if (this.Age != other.Age)
{
return this.Age.CompareTo(other.Age);
}
else return 0;
}
}
// 客戶端
peopleList.Sort();
// OUTPUT:
// 李四 18
// 王五 16
// 王五 30
// 張三 22
// 張三 24
List
.Sort(IComparer
Comparer)
區別於上述繼承IComparable
- 方法:新建PeopleComparer類、繼承IComparer
接口、實現Compare()方法 - 原理:list.Sort()將PeopleComparer類的實例作為參數,在內部使用Compare()方法進行兩兩比較,最終實現排序(注:上述方法為CompareTo(),此處為Compare()方法)
// 自定義比較方法類
class PeopleComparer : IComparer<People>
{
// 區別於CompareTo()單參數,此處為雙參數
public int Compare(People x, People y)
{
if (x.Name != y.Name)
{
return x.Name.CompareTo(y.Name);
}
else if (x.Age != y.Age)
{
return x.Age.CompareTo(y.Age);
}
else return 0;
}
}
// 客戶端
// 傳入參數為自定義比較類的實例
peopleList.Sort(new PeopleComparer());
// OUTPUT:
// 李四 18
// 王五 16
// 王五 30
// 張三 22
// 張三 24
同理,List<T>.Sort(int index, int count, IComparer<T> Comparer)
方法的參數:待排元素起始索引、待排元素個數、排序方法
List
.Sort(Comparison
comparison)
區別於上述繼承接口的方法,此方法的參數為 泛型委托 Comparison<T>
- 委托原型:
public delegate int Comparison<in T>(T x, T y);
- 方法:依照委托的使用方法,首先創建委托實例MyComparison,並綁定到自定義的比較方法PeopleComparison()上,最終調用list.Sort()時 將委托實例傳入
- 原理:list.Sort()根據傳入的委托方法,進行兩兩元素比較最終實現排序
// 客戶端
class Client
{
// 自定義比較方法
public static int PeopleComparison(People p1, People p2)
{
if (p1.Name != p2.Name)
{
return p1.Name.CompareTo(p2.Name);
}
else if (p1.Age != p2.Age)
{
return p1.Age.CompareTo(p2.Age);
}
else return 0;
}
static void Main(string[] args)
{
/* 創建list ... */
// 創建委托實例並綁定
Comparison<People> MyComparison = PeopleComparison;
// 傳入該實例實現比較方法
peopleList.Sort(MyComparison);
// OUTPUT:
// 李四 18
// 王五 16
// 王五 30
// 張三 22
// 張三 24
}
}
此外,既然Comparison<T>
是泛型委托,則完全可以用 Lambda表達式 進行描述:
// Lambda表達式實現Comparison委托
peopleList.Sort((p1, p2) =>
{
if (p1.Name != p2.Name)
{
return p2.Name.CompareTo(p1.Name);
}
else if (p1.Age != p2.Age)
{
return p2.Age.CompareTo(p1.Age);
}
else return 0;
});
// OUTPUT:
// 張三 24
// 張三 22
// 王五 30
// 王五 16
// 李四 18
總結
雖然本文僅使用了List<T>
一種容器對Sort()方法進行闡述,但是不同容器的使用Sort()的方法大相徑庭,因為核心的原理都是應用兩種接口及泛型委托:
- 兩種接口:
IComparable<T> 、 IComparer<T>
- 泛型委托:
Comparison<T>