在 C# 中重写 Object.Equals() 实例方法;现在代码分析/FxCop 警告 CA2218:“还应该重新定义 GetHashCode”。我应该抑制这个吗?

发布于 2024-08-26 17:34:28 字数 2136 浏览 7 评论 0原文

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

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

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

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

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

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

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

如何纠正违规行为

要解决违反此规则的问题, 提供一个实现 获取哈希码。对于一对物体 相同类型,您必须确保 实现返回相同的结果 如果您实施 Equals,则价值 该对返回 true。

何时抑制警告

-----> 不要抑制此警告 规则。 [箭头&强调我的]

所以,我想知道:为什么我不应该像我计划的那样抑制这个警告?我的案例不值得抑制吗?我不想为这个永远不会被调用的对象编写 GetHashCode() 的实现,因为我的对象永远不会成为集合中的键。如果我想学究气,而不是抑制,那么用抛出 NotImplementedException 的实现重写 GetHashCode() 对我来说是否更合理?


Update: I just looked this subject up again in Bill Wagner's good book Effective C#, and he states in "Item 10: Understand the Pitfalls of GetHashCode()":

如果您定义的类型不会 曾经被用作钥匙 容器,这没关系。类型 代表窗口控件、网络 页面控件或数据库连接 不太可能被用作密钥 收藏。在这些情况下,请执行以下操作: 没有什么。所有引用类型都会 有一个正确的哈希码,甚至 如果效率非常低的话。 [...] 在 您创建的最多类型,最好的 方法是避免存在 完全GetHashCode()。

...这就是我最初的想法,我不需要总是关心 GetHashCode() 。

I've got a complex class in my C# project on which I want to be able to do equality tests. It is not a trivial class; it contains a variety of scalar properties as well as references to other objects and collections (e.g. IDictionary). For what it's worth, my class is sealed.

To enable a performance optimization elsewhere in my system (an optimization that avoids a costly network round-trip), I need to be able to compare instances of these objects to each other for equality – other than the built-in reference equality – and so I'm overriding the Object.Equals() instance method. However, now that I've done that, Visual Studio 2008's Code Analysis a.k.a. FxCop, which I keep enabled by default, is raising the following warning:

warning : CA2218 : Microsoft.Usage : Since 'MySuperDuperClass'
redefines Equals, it should also redefine GetHashCode.

I think I understand the rationale for this warning: If I am going to be using such objects as the key in a collection, the hash code is important. i.e. see this question. However, I am not going to be using these objects as the key in a collection. Ever.

Feeling justified to suppress the warning, I looked up code CA2218 in the MSDN documentation to get the full name of the warning so I could apply a SuppressMessage attribute to my class as follows:

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

However, while reading further, I noticed the following:

How to Fix Violations

To fix a violation of this rule,
provide an implementation of
GetHashCode. For a pair of objects of
the same type, you must ensure that
the implementation returns the same
value if your implementation of Equals
returns true for the pair.

When to Suppress Warnings

-----> Do not suppress a warning from this
rule.
[arrow & emphasis mine]

So, I'd like to know: Why shouldn't I suppress this warning as I was planning to? Doesn't my case warrant suppression? I don't want to code up an implementation of GetHashCode() for this object that will never get called, since my object will never be the key in a collection. If I wanted to be pedantic, instead of suppressing, would it be more reasonable for me to override GetHashCode() with an implementation that throws a NotImplementedException?


Update: I just looked this subject up again in Bill Wagner's good book Effective C#, and he states in "Item 10: Understand the Pitfalls of GetHashCode()":

If you're defining a type that won't
ever be used as the key in a
container, this won't matter. Types
that represent window controls, web
page controls, or database connections
are unlikely to be used as keys in a
collection. In those cases, do
nothing. All reference types will
have a hash code that is correct, even
if it is very inefficient. [...] In
most types that you create, the best
approach is to avoid the existence of
GetHashCode() entirely.

... that's where I originally got this idea that I need not be concerned about GetHashCode() always.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

留一抹残留的笑 2024-09-02 17:34:28

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

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

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

话虽如此:来吧。您输入问题所花费的时间可能比正确实现问题所花费的时间还要多。去做就对了。

If you are reallio-trulio absosmurfly positive that you'll never use the thing as a key to a hash table then your proposal is reasonable. Override GetHashCode; make it throw an exception.

Note that hash tables hide in unlikely places. Plenty of LINQ sequence operators use hash table implementations internally to speed things up. By rejecting the implementation of GetHashCode you are also rejecting being able to use your type in a variety of LINQ queries. I like to build algorithms that use memoization for speed increases; memoizers usually use hash tables. You are therefore also rejecting ability to memoize method calls that take your type as a parameter.

Alternatively, if you don't want to be that harsh: Override GetHashCode; make it always return zero. That meets the semantic requirements of GetHashCode; that two equal objects always have the same hash code. If it is ever used as a key in a dictionary performance is going to be terrible, but you can deal with that problem when it arises, which you claim it never will.

All that said: come on. You've probably spent more time typing up the question than it would take to correctly implement it. Just do it.

寻梦旅人 2024-09-02 17:34:28

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

You should not suppress it. Look at how your equals method is implemented. I'm sure it compares one or more members on the class to determine equality. One of these members is oftentimes enough to distinguish one object from another, and therefore you could implement GetHashCode by returning membername.GetHashCode();.

花桑 2024-09-02 17:34:28

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

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

My $0.10 worth? Implement GetHashCode.

As much as you say you'll never, ever need it, you may change your mind, or someone else may have other ideas on how to use the code. A working GetHashCode isn't hard to make, and guarantees that there won't be any problems in the future.

谁把谁当真 2024-09-02 17:34:28

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

As soon as you forget, or another developer who isn't aware uses this, someone is going to have a painful bug to track down. I'd recommend simply implementing GetHashCode correctly and then you won't have to worry about it. Or just don't use Equals for your special equality comparison case.

云之铃。 2024-09-02 17:34:28

GetHashCodeEquals 方法协同工作,为您的类型提供基于值的相等语义 - 您应该一起实现它们。

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

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

The GetHashCode and Equals methods work together to provide value-based equality semantics for your type - you ought to implement them together.

For more information on this topic please see these articles:

Shameless plug: These articles were written by me.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文