簡體   English   中英

Object.Equals:默認情況下一切都是相同的

[英]Object.Equals: everything is equal by default

通過C#第4版 (微軟出版社)閱讀Jeffrey Richter的CLR時 ,作者曾指出,雖然Object.Equals目前檢查身份相同,但Microsoft 應該實現如下方法

public class Object {
    public virtual Boolean Equals(Object obj) {
        // The given object to compare to can't be null
        if (obj == null) return false;

        // If objects are different types, they can't be equal.
        if (this.GetType() != obj.GetType()) return false;

        // If objects are same type, return true if all of their fields match
        // Because System.Object defines no fields, the fields match
        return true;
    }
}

這讓我很奇怪:默認情況下,相同類型的每個非null對象都是相同的 因此,除非被覆蓋:類型的所有實例都相等(例如,所有鎖定對象都相等),並返回相同的哈希碼。 並且假設== on Object仍然檢查引用相等,這意味着(a == b) != a.Equals(b)也會很奇怪。

我認為事物是平等的,如果它是完全相同的(身份)是一個更好的想法,而不僅僅是讓一切都平等,除非被覆蓋。 但這是微軟出版的一本着名的第四版,所以這個想法必定有一些優點。 我閱讀了文本的其余部分,但不禁懷疑: 為什么作者會這樣做呢? 我在這里錯過了什么? Richter的實現優於當前的Object.Equals實現的巨大優勢是什么?

當前默認的Equals()執行所謂的淺比較(或引用比較),然后如果引用不同則不再檢查。

我認為這對於基礎實現是完全可以接受 我當然不會認為這是錯誤的或不完整的。

您引用的Richter的示例1 對於基本System.Object也是完全合法 他的實現的問題是它可以說應該被聲明為抽象2 - 如果你沒有覆蓋它,那么你將在派生對象上最終得到一個不可靠的Equals() (因為Equals()應該進行深度比較) 。 必須在所有派生對象上覆蓋此方法將會有很多工作,因此Microsoft方法作為默認方式更好。 所以在本質上你是正確的:里希特的例子是奇怪的 - 最好默認為不相等而不是反過來(如果人們忘記覆蓋它,默認為true會導致一些相當有趣的行為)。

(僅供參考,這是本書中公布的默認實現)

在此輸入圖像描述



1:里希特是一個聰明的人,他知道他的東西,我一般不會與他說的任何事情爭論。 你必須明白,MS工程師必須長時間地思考很多事情,因為他們知道他們沒有靈活性能夠弄錯,然后才能解決問題。 無論他們多么正確,人們總會在以后再次猜測它們,並提供其他意見。 這並不意味着原始錯誤或替代方案是錯誤的 - 它只是意味着有另一種選擇。

2:當然這意味着沒有基本實現,這很好,因為它本身是不可靠的。

Jeffery Richter談論的是身份平等的價值平等。

具體你問:

所以除非被覆蓋:所有類型的實例都是相同的?

答案是肯定的但是......如同,是的, 它(幾乎)總是應該被覆蓋。

因此,對於大多數類,應該重寫它以進行逐個屬性比較以確定相等性。 對於其他一些真正基於身份的類(如鎖),應該重寫它以使用與現在相同的技術。

關鍵是它幾乎在每種情況下都必須被覆蓋,而這僅僅是足夠困難,笨拙和容易出錯,這可能是微軟沒有使用這種方法的原因。


價值平等優於身份平等的優勢是什么? 這是因為如果兩個不同的對象具有相同的值/內容,那么在諸如Dictionary對象的Keys之類的情況下,它們可以被認為是“相等的”。

或者考慮.Net中的字符串問題,它們實際上是對象,但在較高級別(特別是在VB.net中)得到的處理很像。 當你想比較兩個字符串是否相等時會出現問題,因為99%的時候你真的不在乎它們是不同的對象實例 ,你只關心它們是否包含相同的文本。 所以.Net必須確保字符串比較實際上是這樣的,即使它們確實是對象。

如果要求一個人列出任意類型的所有可識別的不同對象,並且沒有給出任何關於對象是什么或它們將被用於什么的指示,那么唯一普遍適用的測試方法是測試兩個引用是否應該是被認為是指向可識別的不同對象的是Object.Equals(Object) 如果改變一個或多個當前指向X引用,那么它們應該被認為是可識別的兩個引用XY ,以便它們指向Y可能會改變程序行為。

, punctuated and formatted identically, one could likely replace some or all references to the first with references to the second, or vice versa, with little or no effect on program execution beyond the fact that a comparison between two references which point to the same instance may be found to hold identical text much more quickly than could two references which point to different strings that contain identical characters. 例如,如果兩個string實例都包含的整個文本,標點和格式相同,則可能會替換對第一個的一些或所有引用,引用第二個,反之亦然,對其影響很小或沒有影響。程序執行超出了這樣一個事實,即指向同一實例的兩個引用之間的比較可能比兩個引用指向包含相同字符的不同字符串的引用更快地保存相同的文本。

在大多數情況下,如果保存不可變數據的對象相同,則應認為它們是相同的。 存在用於保存可變數據或存在用作身份令牌的對象通常應被視為彼此不同。 鑒於可以定義一個自定義EqualityComparer ,它將被視為不完全等價的等效對象(例如,不區分大小寫的字符串比較器),並且假定需要某些定義等於寬度比嚴格等價的代碼通常應該知道什么類型它正在使用並且等價的定義是合適的,通常最好將Object.Equals報告對象視為不同,除非它們被設計為可替換的(例如,字符串)。

為了使用真實世界的類比,假設給出一張紙,每張紙上寫有車輛識別號,並詢問第一張紙識別的車是否與第二張紙識別的車相同。 。 如果兩張紙的VIN具有相同的VIN,則第一張識別的汽車顯然與第二張識別的汽車相同。 然而,如果它們具有不同的VIN,排除具有多於一個VIN的汽車的任何奇怪可能性,則它們識別不同的汽車。 即使汽車有相同的品牌和型號,選項包,油漆方案等,他們仍然會是不同的汽車。 購買一個人的人無權隨意開始使用另一個人。 有時候知道兩輛車目前是否有相同的選項包等可能是有用的,但如果這是人們想知道的,那就是人們應該問的問題。

猜猜: Object.Equals的當前行為並不是大多數人認為的“平等”。

這種方法存在的主要(唯一?)原因是允許通過偽裝成“==”實現來搜索集合中的項目。 因此,在大多數實際情況下,此實現會出現意外行為(除非您想要查找特定實例是否已在集合中),並強制為您提供自定義比較函數...

可能它是Object的方法,因為技術原因。 即對於Array / Dictionary,假設所有對象都具有Equal / GetHash而不是檢查對象上的某些內容以啟用“Find”功能可能會更快。

可以說它根本不應該在Object上,而只需要可以存儲在集合中的類來實現某種形式的IComparable接口。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM