相等但不可互换

本文关键字:可互换 | 更新日期: 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 的博客文章,我发现我不应该覆盖ItemEqualsGetHashCode方法,因为Item 的实例是可变的并添加到HashSet中。

根据这里的建议,我决定将平等的两个概念分开。

  1. 身份。我没有覆盖ItemEqualsGetHashCode方法。 我保留了这些方法,因为它们在object. 这就是HashSet将使用的。
  2. 等价。我创建了一个名为 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个容器中的项目放在一个字典中怎么办?(,所以我建议坚持设计。如果根据定义,平等仅由其他成员决定,那就去做吧。否则,不会。
您始终可以在以后编写特定于大小写的相等逻辑。请记住,DictionaryListHashSet和其他集合允许您定义自定义IEqualityComparer
还有另一种选择,但要非常小心。覆盖这两种方法,但使GetHashCodeEquals更宽容,这意味着,使不相等的对象具有相同的哈希代码,而不是相反。使GetHashCode忽略父成员,但不忽略Equals。它不会伤害任何东西,然后如果你想忽略字典中的 Parent 成员,写一个IEqualityComparer,以与GetHashCode相同的方式比较它们。