相等但不可互换
本文关键字:可互换 | 更新日期: 2023-09-27 18:26:30
我有两个对象是这样的:
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
实例以查看它们的Value1
值和Value2
值是否匹配。 该方法不会考虑Parent
值,因为我比较的每对实例在这个值上肯定不同,所以这样做会使我正在实现的方法无用,因为它将具有与object.ReferenceEquals
方法相同的结果(false
(。
我的问题如下。 我应该将此方法实现为对象的public override bool Equals( object obj )
方法(以及GetHashCode
(吗? 还是它忽略了其Parent
属性这一事实阻止了我这样做? 为什么或为什么不呢? 我的另一个想法是将其实现为不覆盖任何东西的public bool EqualsIgnoreParent( Item other )
;然后,我可以从自定义比较器调用它。
感谢 mikez 的评论和 Eric Lippert 的博客文章,我发现我不应该覆盖Item
的Equals
或GetHashCode
方法,因为Item
的实例是可变的并添加到HashSet
中。
根据这里的建议,我决定将平等的两个概念分开。
-
身份。我没有覆盖
Item
的Equals
和GetHashCode
方法。 我保留了这些方法,因为它们在object
. 这就是HashSet
将使用的。 -
等价。我创建了一个名为
ValueComparer
的公共单例类,该类嵌套在Item
中并实现IEqualityComparer<Item>
。 这就是我把我描述的比较逻辑放在我的问题中的地方。
我猜你想实现IEquatable。 所以我建议你只是用 Euqals(Item other( 重载 Euqals(对象 other(,同时通过调用 Equals(Item other( 来覆盖 Equals(对象 other(。
当然,让 GetHashCode 始终返回 -1 以强制通过 Equals 进行相等比较。这里解释了为什么需要覆盖 GetHashCode 方法 - 为什么在覆盖 Equals 方法时覆盖 GetHashCode 很重要?
是的,你可以用 Equals 和 GetHashCode 来实现这一点,只要 Value1 和 Value2 不改变,或者你的哈希独立于任何变化。(否则使用您的哈希码的集合将不起作用(
此外,还可以将 Item 类简化为基本成员
class Item {
public string Value1 { get; set; }
public int Value2 { get; set; }
}
并在附加字典中维护关系:(如果您正确实现 GetHashCode,这将很有效(
Dictionary<Item, Container> ContainersOfItems;
我建议拆分 Item 可能更有意义,使其看起来像:
class Container<TContent> {
public HashSet<Item<TContent>> Items { get; }
}
class Item<TContent> {
public Container Parent;
public TContent Content;
}
如果你这样做了,那么你可以定义一个类型来保存你的内容,它有一个适合该类型的Equals
方法,并用一个基于每个Item<TContent>
Content
字段相等的IEqualityComparer<Item<TConcent>>
初始化你的HashSet
。
不要这样做,除非你 100% 确定父成员永远不会有任何区别。可能有很多你没有想到的用例,(如果你需要将2个容器中的项目放在一个字典中怎么办?(,所以我建议坚持设计。如果根据定义,平等仅由其他成员决定,那就去做吧。否则,不会。
您始终可以在以后编写特定于大小写的相等逻辑。请记住,Dictionary
、List
、HashSet
和其他集合允许您定义自定义IEqualityComparer
。
还有另一种选择,但要非常小心。覆盖这两种方法,但使GetHashCode
比Equals
更宽容,这意味着,使不相等的对象具有相同的哈希代码,而不是相反。使GetHashCode
忽略父成员,但不忽略Equals
。它不会伤害任何东西,然后如果你想忽略字典中的 Parent 成员,写一个IEqualityComparer
,以与GetHashCode
相同的方式比较它们。