5

我的 C# 项目中有一个复杂的类,我希望能够在其上进行相等测试。它不是一个微不足道的类;它包含各种标量属性以及对其他对象和集合的引用(例如 IDictionary)。不管怎样,我的班级是封闭的。

为了在我的系统的其他地方启用性能优化(一种避免代价高昂的网络往返的优化),我需要能够将这些对象的实例相互比较以获得相等性——除了内置的引用相等性——等等我正在重写 Object.Equals() 实例方法。但是,既然我已经这样做了,我默认启用的 Visual Studio 2008 的代码分析(又名 FxCop)会发出以下警告:

警告:CA2218:Microsoft.Usage:由于“MySuperDuperClass”重新定义了 Equals,它也应该重新定义 GetHashCode。

我想我理解这个警告的基本原理: 如果我要使用这样的对象作为集合中的,那么哈希码很重要。即看到这个问题但是,我不会将这些对象用作集合中的键。曾经。

感觉有理由抑制警告,我在 MSDN 文档中查找代码 CA2218以获取警告的全名,因此我可以将SuppressMessage属性应用于我的类,如下所示:

    [SuppressMessage("Microsoft.Naming",
        "CA2218:OverrideGetHashCodeOnOverridingEquals",
        Justification="This class is not to be used as key in a hashtable.")]

但是,在进一步阅读时,我注意到以下内容:

如何解决违规问题

要修复违反此规则的行为,请提供 GetHashCode 的实现。对于相同类型的一对对象,如果 Equals 的实现为这对对象返回 true,则必须确保实现返回相同的值。

何时禁止警告

----->不要禁止来自该规则的警告。[箭头和强调我的]

所以,我想知道:为什么我不应该像我计划的那样抑制这个警告? 难道我的案子不需要压制吗?我不想为这个永远不会被调用的对象编写 GetHashCode() 的实现,因为我的对象永远不会成为集合中的键。如果我想变得迂腐而不是压抑,那么用引发 NotImplementedException 的实现来覆盖 GetHashCode() 是否更合理?


更新:我刚刚在 Bill Wagner 的好书Effective C#中再次查看了这个主题,他在“第 10 项:理解 GetHashCode() 的陷阱”中指出:

如果您定义的类型永远不会用作容器中的键,那么这无关紧要。表示窗口控件、网页控件或数据库连接的类型不太可能用作集合中的键。在这些情况下,什么也不做。所有引用类型都会有一个正确的哈希码,即使它非常低效。[...] 在您创建的大多数类型中,最好的方法是完全避免 GetHashCode() 的存在。

...这就是我最初得到这个想法的地方,即我不必总是关心 GetHashCode()。

4

5 回答 5

14

如果您非常肯定您永远不会使用该事物作为哈希表的键,那么您的建议是合理的。覆盖GetHashCode;让它抛出异常。

请注意,哈希表隐藏在不太可能的地方。许多 LINQ 序列运算符在内部使用哈希表实现来加快速度。通过拒绝 GetHashCode 的实现,您也拒绝能够在各种 LINQ 查询中使用您的类型。我喜欢构建使用记忆来提高速度的算法;memoizers 通常使用哈希表。因此,您还拒绝记忆将您的类型作为参数的方法调用的能力。

或者,如果您不想那么苛刻:Override GetHashCode; 使其始终返回零。 满足GetHashCode的语义要求;两个相等的对象总是有相同的哈希码。如果它曾经被用作字典中的键,性能将会很糟糕,但是当它出现时你可以处理这个问题,你声称它永远不会。

一切都说:来吧。您输入问题所花费的时间可能比正确实施问题所花费的时间更多。去做就对了。

于 2010-03-25T15:20:15.073 回答
6

你不应该压制它。看看你的 equals 方法是如何实现的。我确信它会比较班级中的一个或多个成员来确定平等。这些成员之一通常足以将一个对象与另一个对象区分开来,因此您可以GetHashCode通过返回来实现membername.GetHashCode();

于 2010-03-25T14:48:29.347 回答
5

我的 0.10 美元值多少钱?实现 GetHashCode。

尽管您说您永远不会需要它,但您可能会改变主意,或者其他人可能对如何使用代码有其他想法。一个有效的 GetHashCode 并不难制作,并且保证将来不会有任何问题。

于 2010-03-25T14:47:50.037 回答
5

一旦你忘记了,或者其他不知道的开发人员使用了这个,就会有人要追踪一个痛苦的错误。我建议您简单地正确实现 GetHashCode,然后您就不必担心它了。或者只是不要将 Equals 用于您的特殊相等比较案例。

于 2010-03-25T14:47:57.520 回答
4

和方法GetHashCode一起Equals为你的类型提供基于值的相等语义——你应该一起实现它们。

有关此主题的更多信息,请参阅以下文章:

无耻插件:这些文章是我写的。

于 2010-03-25T14:52:53.187 回答