C#中的GetHashCode/Equals重写未被调用

11

我遇到了一个问题,涉及到我重写了一个类的GetHashCode和Equals方法。我使用操作符“==”来验证两个对象是否相等,我期望当它们的哈希码相同时,这应该会调用它们的GetHashCode和Equals方法以验证它们确实相等。

但令我惊讶的是,这两个方法都没有被调用,而相等性测试的结果为false(而实际上应该是true)。

我的重写代码如下:

    public class User : ActiveRecordBase<User>

        [...]

        public override int GetHashCode()
        {
            return Id;
        }

        public override bool Equals(object obj)
        {
            User user = (User)obj;
            if (user == null)
            {
                return false;
            }

            return user.Id == Id;
        }
    }

相等性检查:

    if (x == y) // x and y are both of the same User class
    // I'd expect this test to call both GetHashCode and Equals

4
如果 == 确实调用了您的 Equals 方法,那么它将在对象上使用 == 运算符,从而导致堆栈溢出。 - Guffa
你展示的代码中没有任何需要调用GetHashCode()方法的迹象。只有在将对象用作集合的键时才会调用该方法。 - RenniePet
2个回答

15

==运算符与.GetHashCode().Equals()是完全分离的。

您可能会对Microsoft的重载Equals()和Operator ==的指南感兴趣。

简短地说: 使用.Equals()来实现相等性比较。使用运算符==进行标识比较,或者如果您正在创建一个不可变类型(其中每个相等的实例都可以视为有效地相同)。此外,.Equals()是一个虚方法,可以被子类重写,但是运算符==取决于它使用的表达式的编译时类型。

最后,为了保持一致,在实现.Equals()时必须实现.GetHashCode()。在重载运算符==的同时也要重载运算符!=


1
@Daniel,你关于“==”的建议是不正确的。微软一贯表示,如果您想进行身份比较,必须使用“ReferenceEquals”。例如,考虑字符串。如果您使用字符串构建器创建两个具有相同内容但不同地址的字符串,则“==”将返回TRUE(相等比较),但ReferenceEquals将返回FALSE。“==”通常被视为相等比较,并且通常被实现为执行类的EQUALS所做的任何操作。 - ToolmakerSteve
2
@ToolmakerSteve:从我的回答中的链接可以看出:“当类型是不可变的,也就是说,实例中包含的数据不能被更改时,重载运算符==以比较值相等而不是引用相等可能很有用,因为作为不可变对象,只要它们具有相同的值,它们就可以被视为相同的。在非不可变类型中覆盖运算符==并不是一个好主意。”--这对我来说意味着==只应该在可以安全地混淆相等性和身份的地方(也就是使用不可变值类型的值)才能像等式运算符一样运行。 - Daniel Pryden
@Daniel 我关心你回答中的部分是 "使用操作符==进行身份比较"。在 .Net 中,“身份”具有特定的含义。在底层,它意味着两个相同指针,指向同一内存位置。那就是 ReferenceEquals。如果您使用“身份”一词的含义与此不同,您可能会引起人们的困惑。你所讨论的不是身份,而是等价性。 - ToolmakerSteve
具体来说,两个内容相同但对象不同(在内存中位置不同)的不可变对象,在 .Net 中并没有“相同标识”的定义。即使它们是相同的,如果它们是不可变的,用一个替换另一个也不应该改变程序的结果。 - ToolmakerSteve
@ToolmakerSteve:啊,我现在明白你的困惑了。我的句子并不是试图定义“身份比较”的含义,正如你正确指出的,在.NET中已经有了一个确定的含义,而是提供两个可能使用==的原因:要么用于身份比较,要么用于不可变对象。 - Daniel Pryden
显示剩余3条评论

1

也许需要在你的User类中添加一个方法。

    public virtual bool Equals(User other) 
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return other.Id == Id;
    }

你是不是应该用 override 而不是 virtual - FanManPro

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