共用方式為


覆寫 Equals() 和運算子 == 的方針 (C# 程式設計手冊)

更新:2007 年 11 月

在 C# 中,相等代表著兩種不同的意義,可能是參考相等或實值相等。實值相等是我們對相等的一般認知,意即兩個物件包含相同的值。例如,兩個值為 2 的整數即視為實值相等。參考相等表示不存在兩個可比較的物件,而是有兩個物件參考,並且兩者都參考相同的物件。這只要用簡單的指派就可以看出來,如下列範例所示:

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

在這個程式碼中,只存在一個物件,但對該物件有多個參考:a 和 b。因為他們都參考相同物件,所以就具有參考相等。如果兩個物件有參考相等的關係,則兩者的實值也相等,但實值相等並不保證參考也相等。

若要檢查參考是否相等,請使用 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 的實值型別)。如需詳細資訊,請參閱 可為 Null 的型別 (C# 程式設計手冊))。

Equals 的新實作不應擲回例外狀況。建議任何覆寫 Equals 的類別同時也覆寫 Object.GetHashCode。另外也建議任何類別在實作 Equals (object) 的同時,也應該替本身的型別實作 Equals (type) 以增強效能。例如:

class TwoDPoint : System.Object
{
    public readonly int x, y;

    public TwoDPoint(int x, int y)  //constructor
    {
        this.x = x;
        this.y = y;
    }

    public override bool Equals(System.Object obj)
    {
        // If parameter is null return false.
        if (obj == null)
        {
            return false;
        }

        // If parameter cannot be cast to Point return false.
        TwoDPoint p = obj as TwoDPoint;
        if ((System.Object)p == null)
        {
            return false;
        }

        // Return true if the fields match:
        return (x == p.x) && (y == p.y);
    }

    public bool Equals(TwoDPoint p)
    {
        // If parameter is null return false:
        if ((object)p == null)
        {
            return false;
        }

        // Return true if the fields match:
        return (x == p.x) && (y == p.y);
    }

    public override int GetHashCode()
    {
        return x ^ y;
    }
}

任何可以在基底類別上呼叫 Equals 的衍生類別,在完成比較前也應這麼做。在下列範例中,Equals 會呼叫基底類別 Equals,該類別則會檢查 null 參數,並比較參數的型別與衍生類別的型別。這時,衍生類別的 Equals 實作將必須檢查衍生類別上所宣告的新資料欄位:

class ThreeDPoint : TwoDPoint
{
    public readonly int z;

    public ThreeDPoint(int x, int y, int z)
        : base(x, y)
    {
        this.z = z;
    }

    public override bool Equals(System.Object obj)
    {
        // If parameter cannot be cast to ThreeDPoint return false:
        ThreeDPoint p = obj as ThreeDPoint;
        if ((object)p == null)
        {
            return false;
        }

        // Return true if the fields match:
        return base.Equals(obj) && z == p.z;
    }

    public bool Equals(ThreeDPoint p)
    {
        // Return true if the fields match:
        return base.Equals((TwoDPoint)p) && z == p.z;
    }

    public override int GetHashCode()
    {
        return base.GetHashCode() ^ z;
    }
}

覆寫運算子 ==

根據預設,運算子 == 會藉由判斷兩個參考是否表示相同的物件,以測試參考是否相等。因此,參考型別 (Reference Type) 並不需要實作運算子 == 來取得這項功能。當型別為不可變,也就是包含在執行個體中的資料無法變更時,以多載運算子 == 比較實值相等 (而非參考相等) 會相當有用,因為做為不可變的物件,只要具有相同的值,都會被視為是相同的。覆寫非不可變之型別中的運算子 ==,並不是個好主意。

多載運算子 == 實作不應擲回例外狀況。任何多載運算子 == 的型別也應多載運算子 !=。例如:

//add this code to class ThreeDPoint as defined previously
//
public static bool operator ==(ThreeDPoint a, ThreeDPoint b)
{
    // If both are null, or both are same instance, return true.
    if (System.Object.ReferenceEquals(a, b))
    {
        return true;
    }

    // If one is null, but not both, return false.
    if (((object)a == null) || ((object)b == null))
    {
        return false;
    }

    // Return true if the fields match:
    return a.x == b.x && a.y == b.y && a.z == b.z;
}

public static bool operator !=(ThreeDPoint a, ThreeDPoint b)
{
    return !(a == b);
}
注意事項:

多載運算子 == 時常見的錯誤,就是使用 (a == b)、(a == null) 或 (b == null) 來檢查參考相等。這樣會改成建立對多載運算子 == 的呼叫,並會造成無限的迴圈。請使用 ReferenceEquals 或將型別轉換為 Object,避免產生這種迴圈。

請參閱

概念

C# 程式設計手冊

參考

陳述式、運算式和運算子 (C# 程式設計手冊)

運算子 (C# 程式設計手冊)

可多載的運算子 (C# 程式設計手冊)

== 運算子 (C# 參考)

() 運算子 (C# 參考)