C#排序比較

與C#定義了相等性比較規範同樣,C#也定義了排序比較規範,以肯定一個對象與另外一個對象的前後順序。排序規範以下html

  • IComparable接口(包括IComparable接口和IComparable<T>接口)
  • >和<運算符

當須要實現排序算法時,使用IComparable接口。在下面的例子中,Array.Sort靜態方法能夠調用,是由於System.String類實現了IComparable接口。算法

string[] colors={"Green", "Red", "Blue"};
Array.Sort(colors)
foreach(string c in colors)
    Console.Write(c+ " ");

而<和>運算符比較特殊,由於他們通常用於比較數字類型。由於大於和小於運算符會被靜態地解析,所以它們「產生」出高效的代碼,適用於複雜計算的場景。ide

.NET Framework還提供了插件式的排序協議--IComparer接口。IComparable接口與IComparer接口的差異相似與IEquatable和IEqualityComparer接口 (關於IEqutable接口和IEqualityComparer接口,請參考C#相等性:http://www.cnblogs.com/yang_sy/p/3582946.html)this

 

1. IComparable接口

IComparable接口的定義以下spa

public interface IComparable

    int CompareTo(Object obj);
}

public interface IComparable<in T>
{
    int CompareTo(T other);
}

這兩個接口定義了相同的功能。對於值類型,IComparable<T>接口效率高於ICompare接口。上面的兩個接口的CompareTo方法都按照下面的方式運行:插件

  • 若是a排在b後面,那麼a.CompareTo(b)返回1
  • 若是a和不同,那麼返回0
  • 若是a排在不前面,那麼返回-1

咱們來看下面的示例代碼:code

IList<Staff> staffs = new List<Staff> 
{
    new Staff{FirstName="AAA", Title="Manager", Dept="Sale"},  
    new Staff{FirstName="BBB", Title="Accountant", Dept="Finance"},
    new Staff{FirstName="CCC", Title="Accountant", Dept="Finance"},
};

Console.WriteLine("BBB".CompareTo(staffs[0].FirstName)); // 1
Console.WriteLine("BBB".CompareTo(staffs[1].FirstName)); // 0
Console.WriteLine("BBB".CompareTo(staffs[2].FirstName)); // -1

C#的大部分基本類型都實現了IComparable接口和IComparable<T>接口。不少自定義類型一樣也實現了該接口,這樣便於排序。htm

IComarable與Equals對象

假設一個類型重寫了Equals方法並實現了IComparable接口。那麼你確定但願當Equals返回true時,CompareTo應當返回0。而Equals返回false時,CompareTo能夠返回任何值。blog

換句話說,相等性比對比性更嚴格;反之則不會。所以,當CompareTo說「兩個對象相等」時,Equals會說「這兩個對象不必定相等」。一個很好的例子來自System.String類。String.Equals方法和==運算符使用序號排序規則比較字符串--也就是經過每一個字符的Unicode的值進行排序。而String.CompareTo方法,卻使用不那麼嚴格的基於文化區域(culture-dependent)進行比較。對於大多數計算機,字符ǖ和ṻ,Equals返回False,而CompareTo返回0

你能夠實現經過IComparer接口,從而完成特定的排序算法。自定義IComparer接口的實現,進一步加大了CompareTo和Equals方法之間的差別。好比不區分大小寫的字符串比較器,對於A和a,將返回0. 這也從反面印證了,ComparTo方法不如Equals方法嚴格。

 

2. <和>運算符

一些類型,定義了<和>運算符,好比:

bool after2010 = DateTime.Now > new DateTime(2010, 1, 1);
Console.WriteLine(after2010);

當實現<和>運算符以後,你須要保證<和>運算符與IComparable接口保持一致。這也是.NET Framework的標準。

一樣地,當一個類型重載了<和>運算符,那麼也要求實現IComparable接口,而反之則不須要。實際上,大多數.NET類型實現了IComparable接口,並無重載<和>運算符。這(排序比較)與相等性比較不同:

  • 在實現相等性比較時,若是重載了Equals方法,那麼通常都重載==運算符
  • 而在實現排序性比較時,若是實現了CompareTo方法,通常不要求重載<運算符和>運算符

通常地,只有在下面的情形中,才須要重載<運算符和>運算符:

  • 一個類型自己包含大於和小於這樣的概念
  • 執行前後順序比較的方式是惟一的
  • 結果不會隨文化區域(Cultures)變化而變化

System.Stirng類型不知足最後一條,所以string不支持>操做和<操做。所以 「beck」 > 「Anne」,編譯時會拋出錯誤。

 

3. 實現IComparable接口

下面的實例代碼中,結構Note表示一個音樂的註釋,它實現了IComparable接口,還重載了<運算符和>運算符。爲了實例的完整性,咱們還重寫了Equals和GetHashCode方法,以及重載了==和!=運算符,經過這個例子,你能夠全面的瞭解排序比較。

internal struct Note : IComparable, IComparable<Note>, IEquatable<Note>
{

    private int semitonesFromA;
    public int SemitonesFromA
    {
        get { return semitonesFromA; }
    }

    public Note(int semitonesFromA)
    {
        this.semitonesFromA = semitonesFromA;
    }

    // generic IComparable<T>
    public int CompareTo(Note other)
    {
        if (Equals(other))
            return 0;
        return SemitonesFromA.CompareTo(other.SemitonesFromA);
    }

    // non-generic IComaparable
    public int IComparable.CompareTo(object other)
    {
        if (!(other is Note))
            throw new InvalidOperationException("CompareTo: Not a note");
        return CompareTo((Note)other);
    }

    public static bool operator <(Note n1, Note n2)
    {
        return n1.CompareTo(n2) < 0;
    }

    public static bool operator >(Note n1, Note n2)
    {
        return n1.CompareTo(n2) > 0;
    }

    // for IEquatable
    public bool Equals(Note other)
    {
        return this.SemitonesFromA == other.SemitonesFromA;
    }

    // override Object.Equals
    public override bool Equals(object other)
    {
        if (!(other is Note))
            throw new InvalidOperationException("CompareTo: Not a note");
        return Equals((Note)other);
    }

    public override int GetHashCode()
    {
        return SemitonesFromA.GetHashCode();
    }

    public static bool operator ==(Note n1, Note n2)
    {
        return n1.Equals(n2);
    }

    public static bool operator !=(Note n1, Note n2)
    {
        return !(n1 == n2);
    }
}
相關文章
相關標籤/搜索