C# HashSet未调用重写的Equals方法

C# HashSet未调用重写的Equals方法,c#,C#,我有一个名为“x”的类,它覆盖了Equals(),如下所示: public override bool Equals(object obj) { if(obj is x) { return ((obj as x).key == this.key); } return false; } x SearchKey = new x(key); 当以下扩展方法尝试使用上述重写进行比较时,将不

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

    public override bool Equals(object obj)
    {
        if(obj is x)
        {
            return ((obj as x).key == this.key);
        }
        return false;
    }
x SearchKey = new x(key);
当以下扩展方法尝试使用上述重写进行比较时,将不使用Equals()

    public static bool Contains(this HashSet<x> set, char key)
    {
        x SearchKey = new x(key);
        return set.Contains(SearchKey);
    }
你能解释一下这种行为吗


我原以为,Equals()将针对x本身的实例被调用,因为它是对象的子集。我错过了什么

您可能必须重写GetHashCode;)
在hashSet中,比较方法是hashcode

首先也是最重要的,正如其他人所指出的,您还必须重写
GetHashCode
。比如:

public override int GetHashCode()
{
    return key.GetHashCode();
}
    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();
    }

您还必须实现
IEquatable
。集合转到IEquatable接口进行比较,该接口是类型安全的,在比较值类型时不会导致装箱/取消装箱

如前所述,您也应该重写
GetHashCode
。 如果您使用的是像resharper这样的产品,该工具可以自动为您生成。一种常见的模式是:

public override int GetHashCode()
{
    return key.GetHashCode();
}
    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 Skeets的答案。

您还需要重写GetHashCode。您将同一行写了两次x SearchKey=new x(key)在问题中。您所做的哪些更改使其工作?正如其他人所提到的,您始终需要重写
GetHashCode
Equals
方法。注意
Add
Contains
方法首先调用
GetHashCode
方法,只有当返回的哈希代码在哈希集中不存在时才会调用
Equals
方法。不,在哈希集中,第一个比较方法是按哈希代码,但这不是“比较方法”。如果多个不同的项具有相同的哈希代码,哈希集将不会将它们视为相等,它仍会调用
Equals
记住
哈希代码
可能会发生冲突。此答案不完整
HashSet
中的Contains方法具有不同的签名。我想他的扩展方法会被正确调用。我验证了…我的扩展方法called@KrisVandermotten你说得对,我会更新我的答案!阿迪斯是对的。只要扩展方法具有与在类型上声明的方法不同的签名,就会调用它。只有当扩展名具有完全相同的签名时,types方法才优先。@Zebi right。一开始我看错了签名:)很好的提示,谢谢!我认为R#这样做在这两种情况下都是安全的,但如果你手动这样做,这样做会更简单。+1,但你不必在非泛型
Equals
方法中进行这些引用检查。只是
返回等于(obj作为实体)
将用于类,如果(!(obj是实体))返回false,则
将返回false;回报等于((实体)obj)对于结构(对于结构,您无论如何也不需要在泛型
Equals
方法中进行引用检查)。。