Net有四個判等函數?不少人看到這個標題會對此感到懷疑事實上確是如此Net提供了ReferenceEquals靜態Equals具體類型的Equals以及==操作符這四個判等函數但是這四個函數之間有細微的關系改變其中一個函數的實現會影響到其他函數的操作結果
首先要說的是ObjectReferenceEquals和ObjectEquals這兩個靜態函數對於它們倆來說是不需要進行重寫的因為它們已經完成它們所要得做的操作對於ObjectReferenceEquals這個靜態函數函數形勢如下
public static bool ReferenceEquals( object left object right )
這個函數就是判斷兩個引用類型對象是否指向同一個地址有此說明後就確定了它的使用范圍即只能對於引用類型操作那麼對於任何值類型數據操作即使是與自身的判別都會返回false這主要因為在調用此函數的時候值類型數據要進行裝箱操作也就是對於如下的形式來說
int n =
ObjectReferenceEquals( n n )
這是因為對於n這個數據裝箱兩次而每次裝箱後的地址有不同而造成ObjectReferenceEquals( n n )的結果永遠為false
對於第一個判等函數來說沒有什麼好擴展的因為本身已經很好地完成了它所要做的
對於第二個ObjectEquals這個靜態函數其形式如下
public static bool Equals( object left object right )
按照書中對它的分析其大致函數代碼如下
public static void Equals( object left object right )
{
// Check object identity
if( left == right )
return true
// both null references handled above
if( ( left == null ) || ( right == null ) )
return false
return leftEquals( right )
}
可以說ObjectEquals這個函數完成判等操作需要經過三個步驟第一步是需要根據對象所屬類型的==操作符的執行結果第二步是判別是否為null也是和第一步一樣需要根據類型的==操作符的執行結果最後一步要使用到類型的Equals函數的執行結果也就是說這個靜態函數的返回結果要取決於後面要提到的兩個判等函數類型是否提供相應的判等函數成為這個函數返回結果的重要因素
那麼對於ObjectEquals這個靜態方法來說雖說接受參數的類型也屬於引用類型但是不同於ObjectReferenceEquals函數對於如下的代碼能得出正確的結果
int n =
DebugWriteLine( stringFormat( {} ObjectEquals( n n ) ) )
DebugWriteLine( stringFormat( {} ObjectEquals( n ) ) )
這是因為在此函數中要用到具體類型的兩個判等函數不過就函數本身而言該做的判斷都做了因此不需要去重載添加復雜的操作
為了更好的述說剩下兩個函數先解釋一下等價的意義對於等價的意義就是自反對稱以及傳遞
所謂自反即a == a
而對稱是a == b則b == a
傳遞是 a == bb == c則 a == c
理解等價的意義後那麼在實現類型的判等函數也要滿足這個等價規則
對於可以重載的兩個判等函數首先來介紹的是類型的Equals函數其大致形式如下public override bool Equals( object right )
那麼對於一個類型的Equals要做些什麼操作呢一般來說大致如下
public class KeyData
{
private int nData
public int Data
{
get{ return nData }
set{ nData = value }
} public override bool Equals( object right )
{
//Check null
if( right == null )
return false
//check reference equality
if( objectReferenceEquals( this right ) )
return true
//check type
if( thisGetType() != rightGetType() )
return false
//convert to current type
KeyData rightASKeyData = right as KeyData
//check members value
return thisData == rightASKeyDataData
}
}
如上增加了一個類型檢查即
if( thisGetType() != rightGetType() )這部分這是由於子類對象可以通過as轉化成基類對象從而造成不同類型對象可以進行判等操作違反了等價關系
除此外對於類型的Equals函數來其實並沒有限制類型非要屬於引用類型對於值類型也是可以重載此函數但是我並不推薦主要是Equals函數的參數類型是不可變的也就是說通過此方法值類型要經過裝箱操作而這是比較影響效率的
而對於值類型來說我推薦使用最後一種判等函數即重載運算符==函數其大致形式如下
public static bool operator == ( KeyData left KeyData right )
對於一個值類型而言其的大致形式應該如下
public struct KeyData
{
private int nData
public int Data
{
get{ return nData }
set{ nData = value }
}
public static bool operator == ( KeyData left KeyData right )
{
return leftData == rightData
}
public static bool operator != ( KeyData left KeyData right )
{
return leftData != rightData
}
}
由於==操作與!=操作要同步定義所以在定義==重載函數的時候也要定義!=重載函數這也是Net在判等操作保持一致性那麼對於最後一個判等函數這種重載運算符的方法並不適合引用類型這就是Net經常現象去判斷兩個引用類型不要用==而要用某個對象的Equals函數所以在編寫自己類型的時候要保留這種風格
那麼對於以上介紹的四種判等函數會產生如下類似的對比表格操作結果取決於 適用范圍 建議ObjectReferenceEquals 兩個參數對象是否屬於同一個引用 引用類型 不要用它來判斷值類型數據ObjectEquals 參數類型自身的判等函數 無限制 考慮裝箱操作對值類型數據產生的影響類型的Equals 類型重載函數 無限制 考慮裝箱操作對值類型數據產生的影響類型的==重載 類型重載函數 無限制 不要在引用類型中重載此運算符那麼在編寫類型判等函數的時候要注意些什麼呢給出如下幾點建議
首先要判斷當前定義的類型是否具有判等的意義
其次定義類型的判等函數要滿足等價規則
最後一點值類型最好不要重載定義Equals函數而引用類型最好不要重載定義==操作符
From:http://tw.wingwit.com/Article/program/net/201311/13522.html