在日常開發中經常需要編寫代碼比較不同的對象。例如,有時需要將對象都放到一個集合中,並編寫代碼對集合中的對象進行排序、搜索或者比較。
System.Object類有兩個Equals方法,如下:
1、實例Equals方法(可重寫),代碼如下:
public virtual bool equals(object obj) => RuntimeHelpers.Equals(this, obj)
再看看RuntimeHelpers.Equlas里面調的是什么方法,代碼如下:
[MethodImpl(MethodImplOptions.InternalCall), SecuritySafeCritical] public static extern bool Equals(object o1, object o2);
ok,這里的extern關鍵字告訴你,接下來的不用你考慮了!
2、靜態方法Equals方法,代碼如下:
public static bool Equals(object objA,object objB)=> ((objA==objB) || (((objA!=null) && (objB!=null)) && objA.Equals(objB)))
繼續深入解析代碼,發現objA.Equals調用了上面的實例Equals方法.其實就是在實例Equals方法的基礎上做了非空判斷.然后方法做了靜態化.
到這里源碼解析完畢,由於到extern這一步解析不下去了(博主實力有限),如有知道的請告知!萬分感謝!
由於類型能夠重寫Equals方法,所以Equals方法的邏輯遠比想象的要復雜.下面來舉幾個例子:
1、由於類型能夠重寫Equals方法,所以不能使用它來測試同一性,為了解決這個問題,Object類型提供了ReferenceEquals方法來比較兩個對象的同一性,ReferenceEquals代碼如下:
public static bool ReferenceEquals(object objA,object objB)=>(objA==objB)
注:判斷兩個對象的"同一性"不應該使用C#的==操作符(除非將兩個操作符進行裝箱轉換為Object),因為某個操作數可能重載了==操作符
2、System.ValueType(所有值類型的基類)就重寫了Object的Equals方法,並對兩個對象進行了正確的值相等檢查而不是同一性檢查.代碼如下:
public bool Equals(uint obj)=>(this == obj);
==操作符進行的值檢查.
ValueType.Equals內部會進行一下操作:
1、如果obj實參為null,就返回false;
2、如果this和obj引用的是不同的對象,返回false;
3、針對類型定義的每個實例字段,都將this對象中的值與obj對象中的值進行比較(通過調用對象的Equals方法)。任何字段不相等,就返回false.
4、返回true,ValueType的Equals方法不掉用Object的Equals方法.
上述3步驟,是通過反射實現,由於CLR的反射機制效率不高,所以在定義自己的值類型的時候,應重寫Equals方法來提供自己的實現,從而提高自己類型進行值類型比較時的性能.注:自己的實現不用調用base.Equals().
當我們定義自己的類型時,重寫的Equals方法要符合下面幾個特性:
1、Equals必須自反 x.Equals(x)肯定返回true.
2、Equlas必須對稱 x.Equals(y)和y.Equals(x)必須返回相同的值
3、Equals必須可傳遞 x.Equals(y)返回true,y.Equals(z)返回true則x.Equals(z)也必須返回true.
4、Equals必須一致,比較的兩個值不變,Equals返回值(true或false)也不能變
如果實現的Equals方法不符合上述特性,應用程序就會行為失常.
重寫Equals方法必須做以下幾件事
1、讓類型實現System.IEquatable<T>接口的Equals方法
這個泛型接口允許定義類型安全的Equals方法,通常實現的Equals方法應獲取一個Object參數,以便在內部調用類型安全的Equals方法.
2、重載==和!=操作符方法
通常應實現這些操作符方法,在內部調用類型安全的Equals方法.