對象相等和一致性

在對象比較中,對象一致和對象相等:

簡單的講:相等性(quality)就是兩個對象,它們的值相等。同一性(identity)就是指引用的是否爲同一個對象。 

 

下面是我的讀書筆記: 

 

C# 中有兩種不同的相等:引用相等和值相等。值相等是大家普遍理解的意義上的相等:它意味着兩個對象包含相同的值。例如,兩個值爲 2 的整數具有值相等性。引用相等意味着要比較的不是兩個對象,而是兩個對象引用,這兩個對象引用所引用的是同一個對象。我們這裏把值相等叫做對象的“相等性(equality)”,把引用相等叫做對象的同一性(identity)。 

 

我們都知道在System.Object類型中提供了一個名爲Equals的虛方法,它的作用是在兩個對象相等的情況下返回true,不相等時返回false。等等,這裏的說的“相等”是哪個?是“相等性(equality)”還是“同一性(identity)”。好吧,來看一下代碼吧,System的Object方法是像下面這樣實現的: 

 

public class Object 

public virtual Boolean Equals(Object obj) 

// 如果兩個引用指向的是同一個對象,那麼它們肯定相等 

if (this == obj) return true; 

 

// 假定對象不相等 

return false; 

 

表明上看它好像是實現的很合理:假如this和Obj引用同一對象,自然就是true了,因爲Equals知道一個對象肯定等於它自身。然而,如果this和Obj引用不同的對象哪?,Equals就不能肯定對象是否包含相同的值,所以總結一句話就是:Object的Equals方法實現的只是“同一性(identity)”,而不是“相等性(equality)”。 

 

多麼的令人遺憾啊~!Object的Equals的默認實現並不合理,既然不合理我們就來重寫它吧,先來看看如何在內部正確實現一個Equals方法(我把它概括爲四個字——空、型、值、基): 

 

如果obj參數爲null,就返回false; 

如果this和obj參數引用不同的類型對象,返回false; 

針對類型定義的每個實例字段,將this對象的值和obj對象的值進行對比,任何字段不相等,就返回false; 

調用基類的Equals方法,以便比較它定義的任何字段。如果基類的Equals方法返回false,就返回false;否則返回true; 

再來看一下Object的Equals方法的實現代碼: 

 

public class Object 

public virtual Boolean Equals(Object obj) 

if (obj == null) return false; 

 

if (this.GetType() != obj.GetType()) return false; 

 

// 如果對象屬於相同的類型,那麼在它們的多有字段都匹配的前提下返回true 

// 由於System.Object沒有定義任何字段,所以字段是匹配的 

return true; 

 

其實,這裏要說一下,Microsoft並沒有這樣去實現他的代碼,而是要比這個複雜的多的多。 

 

那麼Equals方法可以在子類中重寫,那麼就不可以用Equals方法來測試同一性(identity)了。怎麼辦啊?Microsoft在Object中提供了一個靜態方法ReferenceEquals,其原型如下: 

 

public class Object 

public static Boolean ReferenceEquals(Object objA, object objB) 

return (objA == objB); 

 

注意了啊,如果想要檢查同一性(identity),那麼務必調用ReferenceEquals,而不應該使用C#的==操作符(除非事先把它們轉化爲Object類型),原因是其中某個操作數的類型可能重載了==操作符,爲其賦予了其它語義。 

 

另外,System.ValueType重寫了Object的Equals方法。並進行了正確的實現來執行相等性(equality)檢查,而不是同一性(identity)檢查。在內部,ValueType的Equals方法是像這樣實現的: 

 

如果obj參數爲null,返回false; 

如果this和obj引用的不同類型的對象,返回false; 

針對類型定義的每個實例字段,都將this對象的值和obj對象中的值進行比較。如果有字段不相等,就返回false; 

返回true;ValueType的Equals的方法不會調用Object的Equals方法。 

順便說一下,ValueType的Equals方法是通過反射技術來完成的。由於CLR反射機制較慢,所以在定義自己的值類型時,應該重寫Equals方法,並提供自己的實現,以便提高性能。當然在自己的實現中不要調用base.Equals。 

 

要重寫Equals,必須遵循一下幾點特性: 

 

x.Equals(x) 返回 true。 (自反性) 

x.Equals(y) 與 y.Equals(x) 返回相同的值。 (對稱性) 

如果 (x.Equals(y) && y.Equals(z)) 返回 true,則 x.Equals(z) 返回 true。 (傳遞性) 

只要不修改 x 和 y 所引用的對象,x.Equals(y) 的後續調用就返回相同的值。 (一致性) 

x.Equals(null) 返回 false。

 

 

另一人回答:對象們都住在不同的房間裏,每個房間只能住一個對象.對象們都被鎖在房間裏,永遠沒有辦法搬家(至少從我們討論的角度來說,這個說法是正確的).所以如果你知道了一個對象的房間號,就能找到對應的對象. 

 

現在假如我們有兩張名片,上面如果寫着相同的房間號,我們就可以斷定,這兩張名片是同一個對象分發出來的,這就是同一性,也就是你所說的一致. 

 

假如1號房裏住着一個值爲1的整數對象, 2號房裏住着另一個值爲2的整數對象,3號房裏住着另另一個值爲1的整數對象.我們又有它們各自的一張名片, 那麼,第一個名片和第三個對應的對象的值是相等的,但是它們不是同一個對象,用你的詞來說,也就是說它們"相等",但不"一致". 

 

如果上面說得太清楚了,那麼這裏讓你再困擾一會吧,哈哈: 

這裏的名片就是引用(i, count之類的變量/字段等的名稱); 

房間所在的大樓就是內存,房間號就是內存地址. 

對象就是內存裏保存的數據.

發佈了45 篇原創文章 · 獲贊 1 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章