重写的 Equals 方法没有被 HashSet 调用

7

我有一个名为“x”的类,它覆盖了Equals(),如下所示:

    public override bool Equals(object obj)
    {
        if(obj is x)
        {
            return ((obj as x).key == this.key);
        }
        return false;
    }

当以下扩展方法尝试使用上述覆盖进行比较时,Equals() 方法不会被使用:
    public static bool Contains(this HashSet<x> set, char key)
    {
        x SearchKey = new x(key);
        return set.Contains(SearchKey);
    }

我只有在将扩展方法的第一行修改为以下内容时才能获得预期行为:
x SearchKey = new x(key);

你能解释一下这个行为吗?

我本来以为,Equals()方法会在x的实例上被调用,因为x是Object的子集。但我错了吗?


3
你需要同时重写 GetHashCode 方法。 - BartoszKP
当重写Equals方法时,为什么重写GetHashCode方法很重要? - sloth
2
你在问题中写了两次相同的代码行 x SearchKey = new x(key);。你做了什么改变让它能够工作? - Eli Arbel
正如其他人所提到的,您始终需要重写GetHashCodeEquals方法。请注意,AddContains方法都会首先调用GetHashCode方法,只有在返回的哈希码不存在于HashSet中时才会调用Equals方法。 - havij
3个回答

3

首先,正如其他人指出的那样,您还需要覆盖 GetHashCode 方法。例如:

public override int GetHashCode()
{
    return key.GetHashCode();
}

HashSet<T>Contains 方法具有不同的签名。我认为他的扩展方法将被正确调用。 - Kris Vandermotten
我验证了...我的扩展方法被调用。 - Aadith Ramia
@KrisVandermotten 你说得对,我会更新我的答案! - nawfal
Aadith 是正确的。只要扩展方法具有与类型上声明的方法不同的签名,它就会被调用。仅当扩展具有完全相同的签名时,类型的方法才优先。 - Zebi
@Zebi 对的。我一开始读错了签名 :) - nawfal

2
你还需要实现IEquatable<T>接口。集合会使用IEquatable接口进行比较,该接口是类型安全的,并且在比较值类型时不会引起装箱/拆箱。
如前所述,你还应该重写GetHashCode方法。如果你使用的是像resharper这样的产品,该工具可以为你自动生成此方法。一个常见的模式如下:
    public virtual bool Equals(Entity other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return other.Id.Equals(Id);
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (!(obj is Entity)) return false;
        return Equals((Entity)obj);
    }

    public override int GetHashCode()
    {
        return Id.GetHashCode();
    }

当您使用接口方法执行所有基于类型的操作,并覆盖Equals(object)仅检查类型不变条件时,然后强制转换并重定向到接口方法。

有关生成哈希代码的一般最佳实践,请参见John Skeet在这里的答案。


非常好的提示,谢谢!我认为R#这样做是为了在两种情况下都更安全,但如果您手动操作,这样会更简单。 - Zebi
1
+1,但是在非泛型的Equals方法中,您不必执行那些引用检查。只需return Equals(obj as Entity);即可适用于类,而对于结构体,则为if (!(obj is Entity)) return false; return Equals((Entity)obj);(对于结构体,您无论如何都不需要在通用的Equals方法中进行引用检查)。 - nawfal

0
你可能需要重写 GetHashCode ;) 在 HashSet 中,比较方法是哈希码。

8
在哈希集中,第一个比较方法是哈希码,但这并不是“比较方法”。如果多个不同的项具有相同的哈希码,则哈希集不会将它们视为相等,仍然会调用 Equals 方法。 - user743382
1
请记住,Hashcode 可能会发生冲突。这个答案是不完整的。 - Sriram Sakthivel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接