C#排序比較


與C#定義了相等性比較規范一樣,C#也定義了排序比較規范,以確定一個對象與另一個對象的先后順序。排序規范如下

  • 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+ " ");

而<和>運算符比較特殊,因為他們一般用於比較數字類型。因為大於和小於運算符會被靜態地解析,因此它們“產生”出高效的代碼,適用於復雜計算的場景。

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

 

1. IComparable接口

IComparable接口的定義如下

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

我們來看下面的示例代碼:

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>接口。很多自定義類型同樣也實現了該接口,這樣便於排序。

IComarable與Equals

假設一個類型重寫了Equals方法並實現了IComparable接口。那么你肯定希望當Equals返回true時,CompareTo應當返回0。而Equals返回false時,CompareTo可以返回任何值。

換句話說,相等性比對比性更嚴格;反之則不會。因此,當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);
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM