標籤:style blog http color 使用 os strong 2014
分析問題
在本書前面的章節中,筆者已經介紹過System.Object中的所有方法。而其中有三個方法被設計用來進行對象的比較,它們是:
(1)、static bool ReferenceEquals(object A,object B)。
(2)、static bool Equals(object A, object B)。
(3)、virtual bool Equals(object obj)。
1、靜態方法:ReferenceEquals實現了類型的引用比較,從參數類型可以看出,它不僅可以用來比較兩個參考型別對象,也可以用來比較兩個實值型別對象。當然,根據實值型別和參考型別的特點,ReferenceEquals方法只有應用在參考型別上時才會有意義,比較兩個實值型別的引用將永遠返回false,無論它們的值是否相等。設定於當下面的代碼被執行時:
int i=0;Console.WriteLine(object.ReferenceEquals(i,i));
得到的結果將仍然是false,讀者可以嘗試從裝箱拆箱的原理去思考原因。
2、靜態方法:Equals是另外一個靜態比較方法,它實現的功能根據不同的類型而有所不同。事實上,Equals方法的功能依靠了執行個體Equals方法的實現,概況地講,靜態Equals方法的內容分為兩步:首先檢查兩個對象是否恒等(==),然後調用其中一個參數對象的執行個體Equals方法來判斷兩個對象是否恒等。
注意
對於實值型別和參考型別來說,檢查兩個對象是否恒等(==)的概念是不同的。對於參考型別來說,這樣的檢查是指是否引用相等,而對於實值型別來說,檢查是指標對內容進行的。
利用發射器,可以清楚地看到靜態方法Equals的代碼:
public static bool Equals(object objA,object objB){ return ((objA==objB)||(((objA!=null)&&(objB!=null))&&(objA.Equals(objB)));}
正如筆者前面分析的,Equals方法首先使用==運算子來檢查參數對象是否相等,然後檢查Null 參考的情況並且調用執行個體方法Equals進行比較。這樣實現的功能完全依賴於執行個體方法Equals的實現,所以讀者將不需要隱藏靜態Equals方法,而應該直接把注意力放在執行個體方法Equals上。
3、執行個體方法:Equals實際上承擔了大部分的比較工作,它是一個虛的執行個體方法,在使用者需要構建自訂比較時,就需要從寫Equals方法。下面是mscorlib.dll的中間代碼,它展示了Object中Equals方法的預設實現:
看上去十分簡單,調用了InternalEquals方法來檢查兩個對象的記憶體位址是否相等,也就是說,Equals的預設實現就是引用相等的比較。筆者在前面章節中已經介紹了,引用比較在某些場合下並不實用,而內容比較可能更加符合商務邏輯的需求,而這個時候,就需要程式員為自己的類型從寫執行個體Equals方法。事實上,所有實值型別的基類ValueType已經重寫了執行個體Equals方法。以下代碼是一個重寫的例子。
using System;namespace TestOverrideEquals{ class OverrideEquals { static void Main() { MyObject o1 = new MyObject(10, "我是字串"); MyObject o2 = new MyObject(10, "我是字串"); //列印引用比較結果 Console.WriteLine("引用比較:{0}", object.ReferenceEquals(o1, o2).ToString()); //列印內容比較結果 Console.WriteLine("自訂的內容比較:{0}", o1.Equals(o2).ToString()); Console.Read(); } } class MyObject { private int _MyInt; private string _MyString; public MyObject() { } public MyObject(int i, string s) { _MyInt = i; _MyString = s; } public override bool Equals(object obj) { //檢查Null 參考 if (obj==null) { return false; } //做引用比較,如何兩者引用相等,勢必內容相等 if (object.ReferenceEquals(this,obj)) { return true; } //檢查兩者的類型是否相等 //考慮到繼承的影響,運行時的類型未必是MyObject //所以使用反射得到確切的類型,犧牲效能來保證正確性 if (this.GetType()!=obj.GetType()) { return false; } //實現內容比較 MyObject right = obj as MyObject; if (_MyInt==right._MyInt&& //String類型雖然是參考型別,但實現了內容比較的== _MyString==right._MyString) { return true; } return false; } }}
在以上代碼中,MyObject重寫了System.Object的執行個體Equals方法,實現了內容上的比較。執行程式可以得到這樣的結果:
注意
以上代碼的編譯會產生一個警告,這是因為MyObject從寫了Equals方法,而沒有重寫GetHashCode方法,這樣做會產生潛在的隱患,這一點將在後續章節中有所覆蓋。
答案
靜態方法ReferenceEquals實現了引用比較。靜態Equals方法實現了比較高效地調用執行個體Equals方法的功能。執行個體Equals方法是一個虛方法,預設的實現是參考型別,類型可以根據需要重寫執行個體Equals方法。實值型別的基類ValueType重寫了Equals方法,實現了內容的比較。