C#經典語法總結(六)Equals() 和運算符 == 的重載準則

 
C# 中有兩種不同的相等:引用相等和值相等。值相等是大家普遍理解的意義上的相等:它意味着兩個對象包含相同的值。例如,兩個值爲 2 的整數具有值相等性。引用相等意味着要比較的不是兩個對象,而是兩個對象引用,且兩者引用的是同一個對象。這可以通過簡單的賦值來實現,如下面的示例所示:
System.Object a = new System.Object();

System.Object b = a;

System.Object.ReferenceEquals(a, b); //returns true

在上面的代碼中,只存在一個對象,但存在對該對象的多個引用:ab。由於它們引用的是同一個對象,因此具有引用相等性。如果兩個對象具有引用相等性,則它們也具有值相等性,但是值相等性不能保證引用相等性。
若要檢查引用相等性,應使用 ReferenceEquals。若要檢查值相等性,請使用 Equals。
重寫 Equals
由於 Equals 是一個虛方法,因此任何類都可以重寫其實現。表示某個值(本質上可以是任何值類型)或一組值(如複數類)的任何類都應該重寫 Equals。如果類型要實現 IComparable,則它應該重寫 Equals
Equals 的新實現應該遵循 Equals 的所有保證:
  • x.Equals(x) 返回 true。
  • x.Equals(y) 與 y.Equals(x) 返回相同的值。
  • 如果 (x.Equals(y) && y.Equals(z)) 返回 true,則 x.Equals(z) 返回 true。
  • 只要不修改 x 和 y 所引用的對象,x.Equals(y) 的後續調用就返回相同的值。
  • x.Equals (null) 返回 false(僅非空值類型。有關更多信息,請參見可以爲 null 的類型(C# 編程指南)。)
Equals 的新實現不應該引發異常。建議重寫 Equals 的任何類同時也重寫 Object.GetHashCode。除了實現 Equals(對象)外,還建議所有的類爲自己的類型實現 Equals(類型)以增強性能。例如:
class TwoDPoint : System.Object

{

    publicreadonly int x, y;



    public TwoDPoint(int x, int y) //constructor

    {

        this.x = x;

        this.y = y;

    }



    publicoverridebool Equals(System.Object obj)

    {

        // If parameter is null return false.

        if (obj == null)

        {

            returnfalse;

        }



        // If parameter cannot be cast to Point return false.

        TwoDPoint p = obj as TwoDPoint;

        if ((System.Object)p == null)

        {

            returnfalse;

        }



        // Return true if the fields match:

        return (x == p.x) && (y == p.y);

    }



    publicbool Equals(TwoDPoint p)

    {

        // If parameter is null return false:

        if ((object)p == null)

        {

            returnfalse;

        }



        // Return true if the fields match:

        return (x == p.x) && (y == p.y);

    }



    publicoverride int GetHashCode()

    {

        return x ^ y;

    }

}



可調用基類的 Equals 的任何派生類在完成其比較之前都應該這樣做。在下面的示例中,Equals 調用基類 Equals,後者將檢查空參數並將參數的類型與派生類的類型做比較。這樣就把檢查派生類中聲明的新數據字段的任務留給了派生類中的 Equals 實現:
class ThreeDPoint : TwoDPoint

{

    publicreadonly int z;



    public ThreeDPoint(int x, int y, int z)

        : base(x, y)

    {

        this.z = z;

    }



    publicoverridebool Equals(System.Object obj)

    {

        // If parameter cannot be cast to ThreeDPoint return false:

        ThreeDPoint p = obj as ThreeDPoint;

        if ((object)p == null)

        {

            returnfalse;

        }



        // Return true if the fields match:

        returnbase.Equals(obj) && z == p.z;

    }



    publicbool Equals(ThreeDPoint p)

    {

        // Return true if the fields match:

        returnbase.Equals((TwoDPoint)p) && z == p.z;

    }



    publicoverride int GetHashCode()

    {

        returnbase.GetHashCode() ^ z;

    }

}



重寫運算符 ==
默認情況下,運算符 == 通過判斷兩個引用是否指示同一對象來測試引用是否相等。因此引用類型不需要實現運算符 == 就能獲得此功能。當類型不可變(即實例中包含的數據不可更改)時,通過重載運算符 == 來比較值是否相等而不是比較引用是否相等可能會很有用,因爲作爲不可變的對象,只要其值相同,就可以將其視爲相同。建議不要在非不可變類型中重寫運算符 ==
重載的運算符 == 實現不應引發異常。重載運算符 == 的任何類型還應重載運算符 !=。例如:
//add this code to class ThreeDPoint as defined previously

//

publicstaticbool operator ==(ThreeDPoint a, ThreeDPoint b)

{

    // If both are null, or both are same instance, return true.

    if (System.Object.ReferenceEquals(a, b))

    {

        returntrue;

    }



    // If one is null, but not both, return false.

    if (((object)a == null) || ((object)b == null))

    {

        returnfalse;

    }



    // Return true if the fields match:

    return a.x == b.x && a.y == b.y && a.z == b.z;

}



publicstaticbool operator !=(ThreeDPoint a, ThreeDPoint b)

{

    return !(a == b);

}



注意:
運算符 == 的重載中的常見錯誤是使用 (a == b)(a == null)(b == null) 來檢查引用相等性。這會調用重載的運算符 ==,從而導致無限循環。應使用 ReferenceEquals 或將類型強制轉換爲 Object 來避免無限循環。
發佈了128 篇原創文章 · 獲贊 0 · 訪問量 21萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章