讀【157個完美化C#的建議】一書的理解筆記 - 010
重點:ICompareable 的用法
流程說明 |
1. ICompareable實作與用途 |
2. ICompareable擴充實作器 |
3. LinQ 與 ICompareable效能比較 (LinQ快兩倍) |
4. 結論 |
1. ICompareable實作與用途
我們建立一個薪資的類別如下 ,並且擴充ICompareable
/// <summary>
/// 薪資類別
/// </summary>
public class Salary : IComparable
{
/// <summary>
/// 國家
/// </summary>
public string Country { get; set;}
/// <summary>
/// 基本薪資
/// </summary>
public int BaseSalary { get; set; }
/// <summary>
/// 紅利
/// </summary>
public int Bonus { get; set; }
public int CompareTo(object obj)
{
Salary staff = obj as Salary;
#region 這段如同下面 BaseSalary.CompareTo(staff.BaseSalary)
if (BaseSalary > staff.BaseSalary)
return 1;
else if (BaseSalary == staff.BaseSalary)
return 0;
else
return -1;
#endregion
//return BaseSalary.CompareTo(staff.BaseSalary);//當被呼叫 .sort()時優先以BaseSalary資料做排序
//return Bonus.CompareTo(staff.Bonus);//當被呼叫 .sort()Bonus
}
}
建立完成後,於主程式宣告以下內容
List<Salary> salaryList = new List<Salary>();
salaryList.Add(new Salary() { BaseSalary = 22000, Country = "B-Taiwan" , Bonus = 50});
salaryList.Add(new Salary() { BaseSalary = 18000, Country = "C-China" , Bonus = 70});
salaryList.Add(new Salary() { BaseSalary = 9000, Country = "A-India" , Bonus =60});
salaryList.Add(new Salary() { BaseSalary = 31000, Country = "D-DUSA" ,Bonus =55});
因為我們覆寫了 ICompareable 呼叫.sort()會執行BaseSalary 的比較,並且由小到大
//排序做法1. IComaparable
salaryList.Sort();//Sort預設內建的排序功能,我們覆寫IComparable
排序後會如下的結果
{ BaseSalary = 9000, Country = "A-India" , Bonus =60}
{ BaseSalary = 18000, Country = "C-China" , Bonus = 70})
{ BaseSalary = 22000, Country = "B-Taiwan" , Bonus = 50})
{ BaseSalary = 31000, Country = "D-DUSA" ,Bonus =55})
2. ICompareable擴充實作器
我們也可以將自訂義的擴充器,實作完成,讓.sort()呼叫,先建立以下
※可以將第1步驟的 Salary類別傳進去比較
/// <summary>
/// 建立一個自訂義的比較器
/// </summary>
public class BonusComparer : IComparer<Salary>
{
/// <summary>
/// 針對Salary 的 Bonus 進行比較
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
public int Compare(Salary x, Salary y)
{
//將Bonus變為Sort()的參考物件
return x.Bonus.CompareTo(y.Bonus);
}
}
就可以在主程式呼叫以下
//排序做法2. IComparer => 非預設性(可擴充的) .sort() 排序
salaryList.Sort(new BonusComparer());
產生的結果就會如下(因為比較器是比較Bonus屬性)
{ BaseSalary = 22000, Country = "B-Taiwan" , Bonus = 50})
{ BaseSalary = 31000, Country = "D-DUSA" ,Bonus =55})
{ BaseSalary = 9000, Country = "A-India" , Bonus =60}
{ BaseSalary = 18000, Country = "C-China" , Bonus = 70})
3. LinQ 與 ICompareable效能比較 (LinQ快兩倍)
我們對List<T> 系列如果想要排序,通常都會用 .OrderBy()的方式,所以我們這邊進行兩種排序的效能比較
以下先將值放進 A、B、C 三項List<T> 中
salaryListTestA : ICompareable 比較器排序
salaryListTestB : Linq 排序,然後執行物件.ToList() (※要進行值的存取,必須ToList())
salaryListTestC : Linq 排序 (※不執行物件.ToList())
//效能比較
List<Salary> salaryListTestA = new List<Salary>();
List<Salary> salaryListTestB = new List<Salary>();
List<Salary> salaryListTestC = new List<Salary>();
Random Dice = new Random();
int DiceVar = 0;//骰子變數
string resultMessage = "";
for (int i = 0; i < 10000000; i++)
{
DiceVar = Dice.Next(0, 10000000);
//兩者存放資料相同才有比較意義
salaryListTestA.Add(new Salary() { Bonus = DiceVar });
salaryListTestB.Add(new Salary() { Bonus = DiceVar });
salaryListTestC.Add(new Salary() { Bonus = DiceVar });
}
Stopwatch sw = Stopwatch.StartNew();
排序的三種作法
//排序做法1. IComparer => 非預設性(可擴充的) .sort() 排序
//計時開始
sw.Restart();
salaryListTestA.Sort(new BonusComparer());
sw.Stop();//計時結束
resultMessage += string.Format("IComparer => 非預設性(可擴充的) .sort() 排序: {0}{1}", sw.Elapsed.ToString(), "\r\n");
//排序做法2. LinQ 轉ToList
sw.Restart();
List<Salary> tempB = salaryListTestB.OrderBy(o => o.Bonus).ToList();
sw.Stop();//計時結束
resultMessage += string.Format("排序做法2. LinQ 轉ToList: {0}{1}", sw.Elapsed.ToString(), "\r\n");
//排序做法3. LinQ 不轉換
sw.Restart();
salaryListTestC.OrderBy(o => o.Bonus);
sw.Stop();//計時結束
resultMessage += string.Format("排序做法3. LinQ 不轉換: {0}{1}", sw.Elapsed.ToString(), "\r\n");
textBox1.Text += resultMessage;
最後印出如下的數據,LinQ的效能在簡單的排序是優化過的。
4. 結論
隨著C# 的進步,LinQ 考量的情境愈來愈多,在很多地方LinQ的效能已經比自己簡單定義的比較器來得快。
如果是大型系統有優化的必要性才會更需要考量ICompareable 的做法。
github連結(Vs2015) : 點我下載