[C#]Effective C# 條款九: 理解幾個相等判斷之間的關係

  • 11667
  • 0
  • C#
  • 2011-02-09

[C#]Effective C# 條款九: 理解幾個相等判斷之間的關係

C#提供了四種不同的函式來判斷兩個物件是否相等:

1.ReferenceEquals靜態方法


	public static bool ReferenceEquals(object left, object right);

 

2.Equals靜態方法


	public static bool Equals(object left,object right);

 

3.Equals方法


	public virtual bool Equals(object right);

 

4.運算子==


	public static bool operator== (MyClass object,MyClass right);

 

前兩個靜態方法我們永遠都不應該去重新定義,後兩個則可視需要去重新定義。

 

以ReferenceEquals來說,其功能為判斷兩個變數指標是否指向同一物件。也就是判斷兩者的物件參考是否相同。如果兩個指標所指的是同一物件的話,則該方法會返回True值。反之,則傳回False。若是以值類別來看,以ReferenceEquals來比較兩個值類別。或是將一個值類別與自身進行比較。其值皆為False。對於ReferenceEquals來說,.預設的功能已經把該方法該有的功能做得很好了,因此我們不需要對此重新定義。

 

而Object.Equals靜態方法則是當我們不知道執行階段類型時,所採用的判斷相等的方法。其實現概念如下:


	public static bool Equals(object left, object right)
{
    //兩個物件參考相等=>return true;
    if (left == right)
        return true;

    //兩個有一個為空=>return false;
    if ((left == null) || (right == null))
        return false;

    //透過left.Equals判斷是否相等
    return left.Equals(right);
}

 

從上述代碼可以看出,當傳入相同的物件參考,該方法會回傳True。而當物件參考不同時,Object.Equals靜態方法會透過叫用left參數的Equals方法來做相等判斷。該方法預設在功能上已經做得很好了,我們也不需對此重新定義。

 

至於非靜態的Object.Equals方法,當預設的處理方式與我們所預期的不同,可以視需求重新定義。像是値類型在預設的情況下,其Equals方法是叫用基底類別ValueType.Equals來處理的。判斷的動作是在不知道類別成員的狀態下去做的。因此,它是利用.NET的反射機制來達到對應的效果,在效能上的表現並不是很好。所以當建立值類別時,我們都應重新定義ValueType.Equals方法。而另一個需重新定義的情況是,當參考類別不想採用物件參考來判斷是否相等,想改用值類型的判斷方式時,我們也可以重新定義Object.Equals方法。

 

Object.Equals方法若要重新定義,一般可以遵循下列的順序來實作:
image

 

程式碼實作上會如下:
 


	public override bool Equals(object obj)
        {
            //檢查參數是否為null
            if (obj == null)
                return false;

            //檢查是否與自身是相同物件
            if (object.ReferenceEquals(this, obj))
                return true;

            //檢查是否型態相等
            if (this.GetType() != obj.GetType())
                return false;

            //比較內容是否相等
            return CompareMembers(this, obj);
        }

 

 

若重新定義的類別其Equals方法不是由System.Object或是System.ValueType提供的話,其實作流程會變為下面這樣:
image

 

程式碼實作上會如下:
 


	public override bool Equals(object obj)
        {
            //檢查參數是否為null
            if (obj == null)
                return false;

            //檢查是否與自身是相同物件
            if (object.ReferenceEquals(this, obj))
                return true;

            //檢查是否型態相等
            if (this.GetType() != obj.GetType())
                return false;

            //叫用基底類別的Equals方法
            if(!base.Equals(obj))
                return false;

            //比較內容是否相等
            return CompareMembers(this, obj);
        }

 

值得注意的是,當我們重新定義了Equals方法時,GetHashCode方法也要跟著重新定義,如此該類別在雜湊函式才可正常運作。

 

最後一個Operator==,則是當我們建立值類別時,就應該重新定義。其理由跟上面重新定義ValueType.Equals方法的一樣。