2013-08-06 56 views
1

我有兩個對象是這樣的:相等,但不能互換

class Container { 
    public HashSet<Item> Items { get; } 
} 

class Item { 
    public Container Parent { get; set; } 
    public string Value1 { get; set; } 
    public int Value2 { get; set; } 
} 

每個Item實例必須屬於Container實例,並且在兩者之間的一個一對多的關係,始終是兩端保持同步。

我現在面臨實施一種方法,比較Item的兩個實例,看他們的Value1Value2值是否匹配。方法將而不是考慮到Parent值,因爲我比較的每對實例在這個值上肯定不同,所以這樣做會使得我實現的方法無用,因爲它將具有與object.ReferenceEquals相同的結果(false)方法。

我的問題如下。我是否應該將此方法作爲對象的public override bool Equals(object obj)方法(以及GetHashCode)來實現?或者它忽略了它的屬性排除了我這樣做的事實?爲什麼或者爲什麼不?我的另一個想法是將其實施爲public bool EqualsIgnoreParent(Item other),它不會覆蓋任何東西;然後我可以從自定義比較器中調用它。

+2

當你將一個可變對象放在一個'HashSet'中時,它會產生一種混亂,因爲正確操作哈希代碼一旦被添加到該集合後就不能改變。 –

+0

我更喜歡爲可變對象實現一個'IEqualityComparer'而不是覆蓋'Equals'。 – CodesInChaos

+0

@CodesInChaos是的,這就是我所做的。請參閱下面的答案。 – HappyNomad

回答

1

感謝mikez's comment和埃裏克利珀的blog post,我發現我不應該重寫ItemEqualsGetHashCode方法,因爲Item的實例是可變的,並添加到HashSet

繼建議here,我決定平等的兩個概念分開。

  1. 身份。我沒有覆蓋ItemEqualsGetHashCode方法。我留下了這些方法,因爲他們在object。這就是HashSet將使用的。
  2. 等效。我創建了一個名爲ValueComparer的公共單身人員課程,它嵌套在Item之內並實施IEqualityComparer<Item>。這是我在我的問題中描述的比較邏輯。
+0

「IEqualityComparer 」的一個好處是'HashSet ','Dictionary '等有構造函數重載,允許您使用它們。 – CodesInChaos

+0

我更喜歡直接從'EqualityComparer '派生而不是直接執行'IEqualityComparer '。 – CodesInChaos

+0

@CodesInChaos你在'EqualityComparer '中找到什麼優勢?也許如果你閱讀[這裏](http://stackoverflow.com/q/5707347/122781)和[這裏](http://stackoverflow.com/a/340526/122781),那麼你會改變主意。 – HappyNomad

0

我想你想實現IEquatable。所以我建議你用Euqals(Item other)重載Euqals(object other),同時通過調用Equals(Item other)覆蓋Equals(object other)。

當然

,使的GetHashCode返回-1總是迫使平等的比較可以通過Equals.here做是爲什麼需要重寫的GetHashCode方法的解釋 - Why is it important to override GetHashCode when Equals method is overridden?

0

是的,你可以與equals實現這一點, GetHashCode,只要Value1和Value2不變或您的散列與任何變化獨立。 (否則使用的hashCode集將無法正常工作)

此外,您可以在項目級減少到基本成員

class Item { 
    public string Value1 { get; set; } 
    public int Value2 { get; set; } 
} 

和維護額外的詞典的關係:(這將是有效的,如果你正確地貫徹執行的GetHashCode)

Dictionary<Item, Container> ContainersOfItems; 
+0

關於你的第二個想法,我看不到用靜態字典替代'Parent'屬性作爲一種改進。我認爲是相反的;它會使代碼複雜化而沒有實際的好處。 – HappyNomad

+0

由於冗餘,維護關係兩端的列表已使代碼複雜化;)好處是, –

+0

...不可用字段不會對任何相等邏輯產生影響。從而獲得安全性,失去靈活性,簡單性和可讀性。與靜態類型語言一樣折衷。 你當然可以隱藏父屬性後面的靜態字典。 –

-1

不這樣做,除非你是100%肯定的是,父成員從來沒有什麼差別。可能有許多用例你沒有想到,(如果你需要將2個容器中的項目放在單個字典中,那麼我會推薦堅持設計。如果根據定義,平等僅由其他成員決定,那就去做吧。否則,不。
您以後可以隨時編寫特定於案例的相等邏輯。請記住,Dictionary,List,HashSet和其他收集允許您定義自定義IEqualityComparer
還有另一種選擇,但要非常小心。重寫這兩種方法,但使GetHashCodeEquals更容忍,這意味着,使不相等的對象具有相同的哈希碼,而不是相反。使GetHashCode忽略父成員,但不是Equals。它不會損害任何內容,然後如果您想忽略字典中的父成員,請編寫一個IEqualityComparer,它們將以與GetHashCode相同的方式進行比較。

+1

'一個選項是隻重寫GetHashCode'你應該總是重寫equals和gethashcode或者都不重寫,從來就不是一個。它們都具有相同的語義含義是很重要的。 – Servy

+0

正如我看到的字典實現,'GetHashCode'用於計算字典中的起始搜索點。從這一點起,字典通過調用「Equals」來搜索該項目。它不需要哈希碼是唯一的,因爲最終的相等性檢查僅由「Equals」進行。 – RoadBump

+0

當然不是,但是'Equals'和'GetHashCode'基於相同的相等概念是很重要的。如果兩個對象相等,但是具有不同的哈希碼,因爲「Equals」方法使用值相等性,並且「GetHashCode」基於引用相等性(反之亦然),則字典出現故障;它突然變得可能添加多個相同的密鑰,或者它會說一個項目不存在時,等等。 – Servy

0

我建議,這可能是更有意義拆分項目,所以它看起來是這樣的:

class Container<TContent> { 
    public HashSet<Item<TContent>> Items { get; } 
} 

class Item<TContent> { 
    public Container Parent; 
    public TContent Content; 
} 

如果你這樣做,那麼你可以定義一個類來保存您的內容具有適當的Equals方法對於這種類型,並初始化您的HashSetIEqualityComparer<Item<TConcent>>其基礎上等於每個Item<TContent>Content字段。

+0

我剛完成實施解決方案。修改對象模型會受到嚴重影響,而且沒有必要。 – HappyNomad