2014-07-22 84 views
7

假設我希望能夠比較兩個整數列表並將一個特定值視爲通配符。在具有通配符的類上實現GetHashCode Equatability

例如 如果-1是一張外卡,然後

{1,2,3,4} == {1,2,-1,4} //returns true

而且我正在寫一個類來包裝這一切的邏輯,所以它實現IEquatable並在public override bool Equals()

相關的邏輯,但我有一直以爲你或多或少不得不實施GetHashCode如果你是凌駕.Equals()。當然,它並沒有被編譯器強制執行,但我一直覺得如果你不這樣做,那麼你就錯了。

除了我沒有看到如何在沒有違約的情況下執行.GetHashCode()(Equal具有不同哈希的對象),或者只是執行return 1

想法?

+16

你打破了平等的概念,它不再是傳遞。在你的情況下,A == B和B == C並不意味着A == C。 – AlexD

+0

也許你需要IEqualityComparer? –

+4

@AlexD應該是一個答案! – nawfal

回答

9

Equals的此實施已無效,因爲它不是transitive。您應該使用默認實現離開Equals,然後編寫一個新方法,如WildcardEquals(如其他答案中的建議)。

通常,只要您更改了Equals,如果您希望能夠將對象存儲在散列表(例如Dictionary<TKey, TValue>)中並使其正常工作,則必須實現GetHashCode。如果你確實知道對象永遠不會以散列表結尾,那麼理論上它是可選的(但在這種情況下,爲了覆蓋它而拋出「NotSupportedException」或總是返回0)會更安全和更清晰。

如果您覆蓋Equals,則一般合約始終應執行GetHashCode,因爲您不能始終確保後面的用戶不會將對象放入哈希表中。

+1

如果您重寫'object.Equals(object)'並且不認爲需要散列碼,則可以使用public override int GetHashCode(){throw new NotSupportedException(); }'或做'public override int GetHashCode(){return 0; }'。根本不覆蓋(儘管有編譯器警告),或者返回'base.GetHashCode()'會導致奇怪的行爲,如果某一天實際上以散列碼很重要的方式使用了類,將會使您感到困惑。 –

+0

謝謝@JeppeStigNielsen,我同意。我已經更新了我的回答 – Rich

+0

感謝Rich--這就是我最終做的。我有兩種情況: - 一種場景,我永遠不會有通配符,但需要IEquatable和GetHashCode(用於GroupBy) - 另一個場景,我可能有通配符,但我不需要IEquatable,我只需要讓布爾回來表明兩者是否平等。 所以我用適當的等效性測試來實現IEquatable,並且已經讓wildcardEquals方法處理了通配符 – Brondahl

2

這是建議,但不是強制性的。如果您不需要GetHashCode的自定義實施,請不要這樣做。

3

唯一可能的想法我看到的是利用至少長度,例如:

public override int GetHashCode() 
{ 
    return this.Length.GetHashCode() 
} 
+0

這似乎很奇怪....例如 - 將這兩個列表添加到HashSet會發生衝突,如果他們有相同的長度,完全不同的元素。 –

+4

@DaveBish是的,但碰撞隻影響性能,不正確。對於這種事情,答案是相當安全的(如果OP想把這個集合放在哈希表中的話)。 – nawfal

+0

這是一個好主意 - 如果我最終需要一個HashCode實現,我會使用它!但根據選定的答案,正確的設計實際上不是這樣做的。 – Brondahl

6

在這種情況下,我想創建,而不是使用運營商新的或擴展的方法,WildcardEquals(other)

我不會推薦隱藏這種複雜性。

1

由於您已經更改了「等於」的含義(在通配符中添加了顯着變化),那麼您已經超出了Equals和GetHashCode的正常使用範圍。這只是一個建議,在你的情況下,它似乎不適合。所以不要擔心。

也就是說,確保你沒有在可能使用GetHashCode的地方使用你的類。如果你沒有注意到它,那會給你帶來很多麻煩並且很難調試。

2

GetHashCode一般來說,只有在您要將類的元素存儲在某種集合(如集合)中時才重要。如果這是這種情況,那麼我認爲你不能實現一致的語義,因爲@AlexD指出平等不再是傳遞性的。

例如,如果您將字符串「A」,「B」和「*」添加到集合中,則您的集合將以一個或兩個元素結束,具體取決於(使用字符串globs而不是整數列表)您添加的訂單。

如果這不是您想要的,那麼我建議將通配符匹配放入新方法(例如EquivalentTo()),而不是重載相等。

2

GetHashCode()總是返回一個常量是實現equals/hashcode約束的唯一'合法'方式。

如果將它放在散列圖或類似的地方,但可能很好(非相等的散列碼暗示非等同,但相同的散列碼暗示沒有),這可能是低效的。

我認爲這是唯一可能的有效選項。散列碼本質上是作爲快速查找事物的鍵存在的,並且由於通配符必須匹配每個項目,因此其查找鍵必須等於每個項目的鍵,因此它們必須全部相同。

正如其他人已經指出的那樣,這不是等於通常的意思,並且打破了許多其他事物可能用於平等的假設(例如傳遞性 - 編輯:事實證明這實際上是契約性要求,所以沒有去),所以至少考慮手動比較這些值,或者與明確分開的相等比較器進行比較是絕對值得的。

+0

「打破了假設」 - 它打破了「等於」合同,所以所有投注都關閉 – Rich

+0

,並沒有意識到Equals實際上明確指出了這一點。 –

4

從邏輯的角度來看,我們打破了平等的概念。它不再是傳遞性的。所以如果是通配符,A==BB==C並不意味着A==C

從技術上看,從GetHashCode()返回相同的值不是不可原諒的。

1

通常預期Equals(Object)IEquatable<T>.Equals(T)應該實現等價關係,使得如果觀察到X等於Y,並且觀察到Y等於Z並且沒有項目已被修改,則X可以是假設等於Z;此外,如果X等於Y且Y確定爲而不是等於Z,則可假定X也不等於Z。通配符和模糊比較方法沒有實現等價關係,因此Equals一般不應該用這樣的語義來實現。

許多集合都會對實現Equals的對象進行分類,只要任何兩個可能相互比較相等的對象始終返回相同的哈希碼,則該對象不會實現等價關係。這樣做通常會要求很多事情會比較不等於返回相同的哈希代碼,儘管取決於支持哪種類型的通配符,可能會在某種程度上分離項目。例如,如果特定字符串支持的唯一通配符表示「一個或多個數字的任意字符串」,則可以通過將所有連續數字序列和/或字符串數​​字通配符字符轉換爲一串「數字」通配符。如果#表示任何數字,則字符串abc123,abc#,abc456和abc#93#22#7將被散列爲與abc#相同的值,但是abc#b,abc123b等可以散列到不同的值值。根據字符串的分佈情況,這種區別可能會或可能不會產生比返回恆定值更好的性能。

請注意,即使一個實現GetHashCode的方式使得相等對象產生相等的散列,但如果等式方法沒有實現等價關係,某些集合仍可能會表現得異常。例如,如果集合foo包含鍵爲「abc1」和「abc2」的項目,則嘗試訪問foo["abc#"]可能會隨意返回第一個項目或第二個項目。嘗試刪除密鑰「abc#」可能會隨意刪除一個或兩個項目,或者可能在刪除一個項目後失敗(預期的後續條件將不會被滿足,因爲abc#即使在刪除後也會在集合中)。

而不是試圖厄運Equals比較哈希碼平等,另一種方法是有其適用於每個可能的通配符的字符串,將匹配至少一個主收集字符串的字符串列表它有可能會字典來比賽。因此,如果有許多匹配abc#的字符串,它們都可能具有不同的哈希碼;如果用戶輸入「abc#」作爲搜索請求,系統將在通配符字典中查找「abc#」並接收與該模式匹配的所有字符串的列表,然後可以在主字典中單獨查找。

相關問題