這個做法在教你如何用 IComparable
首先準備一下測試資料:
void Main()
{
Car[] arrayOfCars = new Car[6]
{
new Car("Ford",1992),
new Car("Fiat",1988),
new Car("Buick",1932),
new Car("Ford",1932),
new Car("Dodge",1999),
new Car("Honda",1977)
};
}
public class Car
{
public int year { get; set; }
public string make { get; set; }
public Car(string Make, int Year)
{
make = Make;
year = Year;
}
}
目前有 IComparableCar
類別實作這兩個介面試試。
public class Car : IComparable<Car>, IComparable
{
public int year { get; set; }
public string make { get; set; }
public Car(string Make, int Year)
{
make = Make;
year = Year;
}
public int CompareTo(object obj)
{
if (!(obj is Car))
throw new ArgumentException("Argument is not a Car", "obj");
Car other = (Car)obj;
return this.CompareTo(other);
}
public int CompareTo(Car other)
{
return String.Compare(this.make, other.make);
}
}
這裡可以用 Linq 的 OrderBy 方法進行測試,目前會使用預設的 Comparer<T>.Default
最後會取得 IComparable<T>
也就是上面寫的 Car
類別的 CompareTo 方法來判斷兩個元素的優先順序,所以結果會輸出由名稱排序。
void Main()
{
Car[] arrayOfCars = new Car[6]
{
new Car("Ford",1992),
new Car("Fiat",1988),
new Car("Buick",1932),
new Car("Ford",1932),
new Car("Dodge",1999),
new Car("Honda",1977)
};
var sorted = arrayOfCars.OrderBy(x => x);
foreach (var stuff in sorted)
{
Console.WriteLine(stuff.make);
}
}
Buick
Dodge
Fiat
Ford
Ford
Honda
也可以多載常用的運算子讓可讀性更加提升。
public class Car : IComparable<Car>, IComparable
{
public int year { get; set; }
public string make { get; set; }
public Car(string Make, int Year)
{
make = Make;
year = Year;
}
public int CompareTo(object obj)
{
if (!(obj is Car))
throw new ArgumentException("Argument is not a Car", "obj");
Car other = (Car)obj;
return this.CompareTo(other);
}
public int CompareTo(Car other)
{
return String.Compare(this.make, other.make);
}
public static bool operator <(Car left, Car right) =>
left.CompareTo(right) < 0;
public static bool operator <=(Car left, Car right) =>
left.CompareTo(right) <= 0;
public static bool operator >(Car left, Car right) =>
left.CompareTo(right) > 0;
public static bool operator >=(Car left, Car right) =>
left.CompareTo(right) >= 0;
}
我們就可以直接比較兩個值的大小,例如下面檢查 Buick
小於 Fiat
結果返回 True。
Console.WriteLine(arrayOfCars[2] < arrayOfCars[1]);
True
目前都是用字串名稱排序方法來進行排序,可以看到我們的測試資料裡面有包含年份,接下來可以試試寫一個根據年份排序的方法。
要做到這樣的效果主要是透過實作 IComparer<T>
介面。
可以在之前的 Car
類別裡面寫一個 Nested Cass YearComparer
並且使用年份來進行比較。
private static Lazy<YearComparer> yearComp = new Lazy<YearComparer>(() => new YearComparer());
public static IComparer<Car> YearCompare => yearComp.Value;
public static Comparison<Car> CompareByYear => (left, right) => left.year.CompareTo(right.year);
private class YearComparer : IComparer<Car>
{
// IComparer<Customer> Members
int IComparer<Car>.Compare(Car left, Car right) =>
left.year.CompareTo(right.year);
}
最後可以利用靜態成員的特性取得專用的 YearComparer
就能看到結果會優先用年份來排序了。
var sorted = arrayOfCars.OrderBy(x => x, Car.YearCompare);
foreach (var stuff in sorted)
{
Console.WriteLine(stuff.make);
}
不過由於現在 Linq 已經支援 OrderBy
, ThenBy
, OrderByDescending
, ThenByDescending
這些好用的方法,
所以現在簡單的排序工作可以直接利用 Linq 就好,有一些特殊的排序在自行實作 IComparer<T>
就好。
var sorted = arrayOfCars
.OrderBy(x => x.year)
.ThenBy(x => x.make);
foreach (var stuff in sorted)
{
Console.WriteLine(stuff.make);
}
Summary
這個做法主要在教如何使用 IComparableIComparer<T>
就好。