首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >应该在可变类型上为GetHashCode实现IEquatable<T>吗?

应该在可变类型上为GetHashCode实现IEquatable<T>吗?
EN

Stack Overflow用户
提问于 2018-03-01 17:43:04
回答 3查看 2K关注 0票数 4

我正在实现IEquatable<T>,而且很难就可变类上的GetHashCode覆盖找到一致意见。

以下资源都提供了一个实现,如果对象更改,GetHashCode将在对象的生存期内返回不同的值:

然而,此链接指出,如果对象是集合的一部分,则GetHashCode应该为可变类型实现而不是,因为它可能会导致不良行为(我也一直这样理解)。

有趣的是,MSDN示例只使用不可变的属性实现GetHashCode,这与我的理解是一致的。但我不明白为什么其他资源不涵盖这个问题。难道他们就错了吗?

如果一个类型根本没有不可变的属性,编译器警告我重写GetHashCode时缺少Equals(object)。在这种情况下,我应该实现它,只调用base.GetHashCode(),或者禁用编译器警告,或者我遗漏了什么,GetHashCode应该总是被重写和实现?实际上,如果建议是不应该为可变类型实现GetHashCode,那么为什么还要为不可变类型实现呢?与默认的GetHashCode实现相比,它仅仅是为了减少冲突,还是实际上增加了更明显的功能?

总结一下我的问题,我的困境是,在可变对象上使用GetHashCode意味着,如果对象上的属性发生变化,它可以在对象的生存期内返回不同的值。但是,不使用它意味着比较可能等效的对象的好处已经丧失,因为它总是返回一个唯一的值,因此集合将总是回到使用Equals操作。

在输入了这个问题之后,另一个问题弹出了“类似的问题”框,这个框似乎是针对同一个主题的。答案似乎非常明确,因为在GetHashCode实现中只应该使用不可变的属性。如果没有,那就不要写。Dictionary<TKey, TValue>仍将正常工作,尽管不能达到O(1)的性能。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2018-03-01 22:01:46

可变类在字典和其他依赖于GetHashCode和Equals的类中工作得很糟糕。

在您所描述的场景中,使用可变对象,我建议使用以下方法之一:

代码语言:javascript
复制
class ConstantHasCode: IEquatable<ConstantHasCode>
{
    public int SomeVariable;
    public virtual Equals(ConstantHasCode other)
    {
        return other.SomeVariable == SomeVariable;
    }

    public override int GetHashCode()
    {
        return 0;
    }
}

代码语言:javascript
复制
class ThrowHasCode: IEquatable<ThrowHasCode>
{
    public int SomeVariable;
    public virtual Equals(ThrowHasCode other)
    {
        return other.SomeVariable == SomeVariable;
    }

    public override int GetHashCode()
    {
        throw new ApplicationException("this class does not support GetHashCode and should not be used as a key for a dictionary");
    }
}

在第一种情况下,Dictionary的工作(几乎)和预期一样,在查找和插入时会导致性能损失:在这两种情况下,对于字典中已经存在的每个元素都会调用相等,直到比较返回true为止。实际上,您正在恢复列表的性能。

第二种方法是告诉程序员使用您的类“不行,不能在字典中使用”。不幸的是,据我所知,没有在编译时检测它的方法,但是这将在代码第一次向字典中添加元素时失败,很可能在开发过程中很早就会失败,而不是那种只发生在有一组无法预测的输入的生产环境中的错误。

最后但并非最不重要的一点是,忽略“可变”问题,使用成员变量实现GetHashCode :现在您必须意识到,在使用字典时,您不能随意修改该类。在某些情况下,这是可以接受的,而在另一些情况下则是不可接受的

票数 2
EN

Stack Overflow用户

发布于 2018-03-01 18:13:32

这完全取决于你说的是哪种collection类型。对于我的答案,我假设您是在讨论基于Hash Tablecollections,特别是在.NET、DictionaryKey计算方面。

因此,如果您修改key(假设您的key是一个进行自定义HashCode计算的类),最好的方法是查看.NET源代码。从.NET源代码中我们可以看到,您的key value pair现在被包装到Entry结构中,该结构包含hashcode,该结构是根据您的值的addition计算的。这意味着,如果在添加密钥的时间之后更改了HashCode值,那么它将无法在dictionary中找到值。

证明这一点的代码:

代码语言:javascript
复制
    static void Main()
    {
        var myKey = new MyKey { MyBusinessKey = "Ohai" };
        var dic = new Dictionary<MyKey, int>();
        dic.Add(myKey, 1);
        Console.WriteLine(dic[myKey]);
        myKey.MyBusinessKey = "Changing value";
        Console.WriteLine(dic[myKey]); // Key Not Found Exception.
    }

    public class MyKey
    {
        public string MyBusinessKey { get; set; }
        public override int GetHashCode()
        {
            return MyBusinessKey.GetHashCode();
        }
    }

.NET源参考

所以回答你的问题。您希望有不可变的值作为hashcode计算的基础。

另外一点,对于自定义类,hashcode如果不覆盖GetHashCode,则将基于对object的引用。因此,对不同对象返回相同的hashcode (在底层值上相同)的关注可以通过overriding GetHashCode方法来缓解,并根据业务密钥计算HashCode。例如,您将有两个字符串属性,为了计算哈希代码,您可以使用concat strings和调用basestring GetHashCode方法。这将保证对于相同的hashcode的基础值,您将得到相同的object

票数 0
EN

Stack Overflow用户

发布于 2018-03-02 11:09:01

经过多次讨论并阅读了其他关于这个主题的答案,最终此ReSharper帮助页面对我来说总结得非常好:

MSDN文档 of GetHashCode()方法不显式要求此方法的重写返回在对象生存期内永不更改的值。具体来说,它说: 对象的GetHashCode方法必须始终如一地返回相同的哈希码,只要没有修改对象状态来确定对象的相等方法的返回值。 另一方面,它说哈希代码不应该更改,至少当对象在集合中时: *您可以为不可变的引用类型重写GetHashCode。通常,对于可变引用类型,只有在以下情况下才应重写GetHashCode:

  • 您可以从不可变的字段中计算哈希代码;或
  • 您可以确保在对象包含在依赖其哈希代码的集合中时,可变对象的哈希代码不会更改。

但是,为什么首先需要重写GetHashCode()?通常,如果您的对象将在哈希表中用作字典中的键等,则很难预测何时将对象添加到集合中,以及对象将保留在集合中多长时间。

尽管如此,如果您希望处于安全的位置,请确保GetHashCode()的重写在对象的生存期内返回相同的值。通过指向GetHashCode().实现中的每个非只读字段或非获取属性,ReSharper将在此帮助您。如果可能的话,ReSharper也会建议快速修复使这些成员成为只读/只读的。

当然,如果快速修复是不可能的,这并不意味着该做什么。然而,它确实表明,这些快速修复只应“在可能的情况下”使用,这意味着检查可能会被抑制。Gian Paolo对此的回答建议抛出一个异常,这将防止类被用作键,如果无意中将其作为密钥使用,它将在开发早期出现。

但是,GetHashCode在其他情况下使用,例如当对象的实例作为参数传递给模拟方法设置时。因此,唯一可行的选择是使用可变值实现GetHashCode,并将责任放在代码的其余部分,以确保对象在用作键时不会发生变异,或者根本不将其用作键。

票数 -3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49055673

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档