Chapter 3 - Item 26 : Implement Classic Interfaces in Addition to Generic Interfaces

Effective C# (Covers C# 6.0), (includes Content Update Program): 50 Specific Ways to Improve Your C#, 3rd Edition By Bill Wagner 讀後心得

設計類別時,不只實作泛型版本,非泛型版本也應一起實作;以支援較舊版本。

public class NameTest :
    IComparable<NameTest>,
    IEquatable<NameTest>,
    IComparable
{
    public string First { get; set; }
    public string Last { get; set; }
    public string Middle { get; set; }

    public static bool checkEquality( object left, object right ) =>
        left?.Equals( right ) ?? ( right == null );

    public static bool checkEquality<T>( T left, T right )
        where T : IEquatable<T> =>
        left?.Equals( right ) ?? ( right == null );
    
    // 實作 IEquatable<NameTest> 
    public bool Equals( NameTest other )
    {
        if ( ReferenceEquals( this, other ) )
            return true;

        if ( ReferenceEquals( other, null ) )
            return false;

        return Last == other.Last &&
            First == other.First &&
            Middle == other.Middle;
    }

    // 需增加明確型別判斷,避免呼叫到 Equals( object ) 版本。
    // NameTest 的子類別皆可轉型成 NameTest,但型別依然不同。
    public override bool Equals( object obj ) =>
        obj.GetType( ) == typeof( NameTest ) ? // Check Runtime Type.
        Equals( obj as NameTest ) : false; // Cast to NameTest.

    // 覆寫 ==,使之與 Equals( NameTest )結果相符。
    public static bool operator ==( NameTest left, NameTest right ) =>
        left?.Equals( right ) ?? ( right == null );

    // 覆寫 !=,使之與 Equals( NameTest )結果相符。
    public static bool operator !=( NameTest left, NameTest right ) =>
        !left?.Equals( right ) ?? ( right != null );

    // 實作 IComparable<NameTest>
    public int CompareTo( NameTest other )
    {
        if ( ReferenceEquals( this, other ) )
            return 0;

        if ( ReferenceEquals( other, null ) )
            return 1; // Any non-null object > null

        var result = Last.CompareTo( other.Last );

        if ( result != 0 )
            return result;

        result = First.CompareTo( other.First );

        if ( result != 0 )
            return result;

        return Middle.CompareTo( other.Middle );
    }
    
    // 顯式定義 IComparable.CompareTo,避免呼叫非泛型版本。
    // 須明確轉型為 IComparable 才能呼叫此方法。
    int IComparable.CompareTo( object obj ) =>
        obj.GetType( ) == typeof( NameTest ) ?
        CompareTo( obj as NameTest ) :
        throw new ArgumentException( "Argument is not a Name object." );

    // 覆寫 <,使之與 CompareTo 結果相符。
    public static bool operator <( NameTest left, NameTest right )
    {
        if ( left == null )
            return right != null;

        return left.CompareTo( right ) < 0;
    }
    
    // 覆寫 <=,使之與 CompareTo 結果相符。
    public static bool operator <=( NameTest left, NameTest right )
    {
        if ( left == null )
            return true;

        return left.CompareTo( right ) <= 0;
    }

    // 覆寫 >,使之與 CompareTo 結果相符。
    public static bool operator >( NameTest left, NameTest right )
    {
        if ( left == null )
            return right == null;

        return left.CompareTo( right ) > 0;
    }
    
    // 覆寫 >=,使之與 CompareTo 結果相符。
    public static bool operator >=( NameTest left, NameTest right )
    {
        if ( left == null )
            return right == null;

        return left.CompareTo( right ) >= 0;
    }
    
    // 由於覆寫了 Equals( object ),也需覆寫 GetHashCode。
    public override int GetHashCode( )
    {
        var hash = 0;

        if ( Last != null )
            hash = Last.GetHashCode( );

        if ( First != null )
            hash ^= First.GetHashCode( );

        if ( Middle != null )
            hash ^= Middle.GetHashCode( );

        return hash;
    }
}
結論:
1. 此節對目前工作內容上較無明確的關連,目前還是以基本比對居多。

2. 若實作了 IEquatable<T> 與 IComparable<T>,也需注意其運算子有無覆寫,以免得到不一致的結果。

3. 顯式定義 IComparable.CompareTo,避免呼叫到非泛型版本。