C#中有一共有四種相等性判斷方法:
//Object中定義的三個方法 public static bool ReferenceEquals(object objLeft, object objRight); public static bool Equals(object objLeft, object objRight); public virtual bool Equals(object obj); //雙等號 public static bool operator == (MyClass left, MyClass right)
①Object.ReferenceEquals(left, right)靜態方法:從名稱中便可知它用來比較兩者是否是相同的引用,我們也永遠不應該去重寫該方法。它對於值類型對象的比較永遠返回false;對於兩個null的比較永遠返回true。
②Object.Equals(left, right)靜態方法:該方法也永遠不需要重寫,因為它最終會把判斷權交給參數left的實例Equls方法(代碼如下),因此沒有必要重寫該方法,只需要保證實例Equals返回想要的結果即可:
public static bool Equals(object left, object right) { if(object.ReferenceEquals(left,right)) //如果引用相同,則必定相等(包含兩個都是null的情況) { return true; } if(object.ReferenceEquals(left ,null)|| object.ReferenceEquals(null,right)) //若只有一個是null,則必定不等 { return false; } return left.Equals(right);//兩個都不是null,且也不是同一個引用,則根據left的實例Equals方法來判斷。 }
③Object中的實例方法Equals,因為它是虛方法,所以可以在其他類中重寫它。該方法的默認實現還是比較兩者是否為同一個引用,即相當於ReferenceEquals。但是微軟在所有值類型的基類System.ValueType中重寫了該方法,用來比較值相等。
- 在值類型中,我們仍然可能要重寫該方法,以提高性能(默認方法的性能不高),另外如果我們的struct中包含了引用類型的成員,則應該重寫該方法。
- 在引用類型中,如果類的實例要作為字典的鍵,則應該重寫Equals方法,使之比較值,而不是引用。
重寫Equals應該遵循的原則:自反性、對稱性、傳遞性。即:a=a;若a=b,則b=a;若a=b,b=c,則a=c;另外兩個對象要么相等要不不等,所以該方法不應該拋出異常。下面是重寫模板:
public class MyClass:IEquatable<MyClass> { //重寫Object中的Equals方法 public override bool Equals(object obj) { if (object.ReferenceEquals(obj, null)) //首先判斷obj不能為空,否則后面對obj調用任何方法都將報錯 { return false; } if(object.ReferenceEquals(this,obj)) //在C#中this永遠不會為空 { return true; } if(this.GetType() != obj.GetType()) { return false; } return this.Equals(obj as MyClass); } //實現IEquatable<T>中的Equals方法 public bool Equals(MyClass other) { //省略 return true; } }
④比較運算符==:對於引用類型,默認是比較引用的(System.String除外),對於值類型默認比較值,對於自定義的結構,如果不顯示重載operator ==方法,則無法使用==。
- 由於在C#中要求比較運算符必須成對重載,重載==運算符的同時也必須重載!=運算符,否則也會產生編譯錯誤。
- 如果要重載的話,運算符"=="、"!=" 與 Equals方法、GetHashCode方法應該同時被重載,因為他們應該保持同樣的相等邏輯。但不要再==中調用Equals,最好是在Equals中調用==。