我最近遇到了这个问题,到目前为止,我一直很高兴地重写相等运算符(==)和/或Equals方法,以查看两个引用类型是否实际包含相同的数据(即看起来相同的两个不同实例)。
自从我更多地进行自动化测试(将参考/预期数据与返回的数据进行比较)以来,我一直在更多地使用它。
查看 MSDN 中的一些编码标准指南 我遇到了一篇文章,建议不要这样做。 现在我明白为什么这篇文章这么说(因为它们不是同一个实例),但它没有回答这个问题:
- 最好的比较方法是什么两种引用类型?
- 我们应该实现IComparable吗? (我还看到提到这应该只保留给值类型)。
- 有什么我不知道的接口吗?
- 我们应该自己推出吗?
非常感谢 ^_^
更新
看起来我误读了一些文档(这是漫长的一天)并覆盖 等于 可能是正确的选择..
如果您正在实施参考
类型,你应该考虑覆盖
引用类型的 Equals 方法
如果您的类型看起来像基本类型
例如点、字符串、BigNumber、
等等。 大多数引用类型应该
不要重载相等运算符,
即使如果它们覆盖 Equals。 然而,
如果您正在实施参考
旨在具有价值的类型
语义,例如复数
类型,你应该覆盖相等性
运算符。
I came across this recently, up until now I have been happily overriding the equality operator (==) and/or Equals method in order to see if two references types actually contained the same data (i.e. two different instances that look the same).
I have been using this even more since I have been getting more in to automated testing (comparing reference/expected data against that returned).
While looking over some of the coding standards guidelines in MSDN I came across an article that advises against it. Now I understand why the article is saying this (because they are not the same instance) but it does not answer the question:
- What is the best way to compare two reference types?
- Should we implement IComparable? (I have also seen mention that this should be reserved for value types only).
- Is there some interface I don't know about?
- Should we just roll our own?!
Many Thanks ^_^
Update
Looks like I had mis-read some of the documentation (it's been a long day) and overriding Equals may be the way to go..
If you are implementing reference
types, you should consider overriding
the Equals method on a reference type
if your type looks like a base type
such as a Point, String, BigNumber,
and so on. Most reference types should
not overload the equality operator,
even if they override Equals. However,
if you are implementing a reference
type that is intended to have value
semantics, such as a complex number
type, you should override the equality
operator.
发布评论
评论(10)
在 .NET 中正确、高效地实现平等并且没有代码重复是很困难的。 具体来说,对于具有值语义的引用类型(即 将等价视为相等的不可变类型),您应该实现
System.IEquatable
接口,您应该实现所有不同的操作(Equals
、GetHashCode
和==
、!=
)。例如,下面是一个实现值相等的类:
上面代码中唯一可移动的部分是粗体部分:
Equals(Point other)
中的第二行和GetHashCode()
代码>方法。 其他代码应保持不变。对于不表示不可变值的引用类,不要实现运算符
==
和!=
。 相反,使用它们的默认含义,即比较对象身份。代码有意甚至等同于派生类类型的对象。 通常,这可能是不可取的,因为基类和派生类之间的相等性没有明确定义。 不幸的是,.NET 和编码指南在这里不是很清楚。 Resharper 创建的代码(在另一个答案中发布)在这种情况下很容易出现不良行为,因为
Equals (object x)
和Equals(SecurableResourcePermission x)
将以不同的方式处理这种情况。为了改变这种行为,必须在上面的强类型
Equals
方法中插入额外的类型检查:Implementing equality in .NET correctly, efficiently and without code duplication is hard. Specifically, for reference types with value semantics (i.e. immutable types that treat equvialence as equality), you should implement the
System.IEquatable<T>
interface, and you should implement all the different operations (Equals
,GetHashCode
and==
,!=
).As an example, here’s a class implementing value equality:
The only movable parts in the above code are the bolded parts: the second line in
Equals(Point other)
and theGetHashCode()
method. The other code should remain unchanged.For reference classes that do not represent immutable values, do not implement the operators
==
and!=
. Instead, use their default meaning, which is to compare object identity.The code intentionally equates even objects of a derived class type. Often, this might not be desirable because equality between the base class and derived classes is not well-defined. Unfortunately, .NET and the coding guidelines are not very clear here. The code that Resharper creates, posted in another answer, is susceptible to undesired behaviour in such cases because
Equals(object x)
andEquals(SecurableResourcePermission x)
will treat this case differently.In order to change this behaviour, an additional type check has to be inserted in the strongly-typed
Equals
method above:看起来您正在使用 C# 进行编码,它有一个名为 Equals 的方法,您的类应该实现该方法,如果您想使用“这两个指针(因为对象句柄就是指针)”之外的其他指标来比较两个对象,相同的内存地址?”。
我从此处获取了一些示例代码:
Java有非常相似的机制。 equals() 方法是 Object 类的一部分,如果您需要这种类型的功能,您的类会重载它。
对于对象来说,重载“==”可能不是一个好主意,因为通常您仍然希望能够进行“这些是相同的指针”比较。 例如,通常依赖这些来将元素插入到不允许重复的列表中,并且如果该运算符以非标准方式重载,则某些框架内容可能无法工作。
It looks like you're coding in C#, which has a method called Equals that your class should implement, should you want to compare two objects using some other metric than "are these two pointers (because object handles are just that, pointers) to the same memory address?".
I grabbed some sample code from here:
Java has very similar mechanisms. The equals() method is part of the Object class, and your class overloads it if you want this type of functionality.
The reason overloading '==' can be a bad idea for objects is that, usually, you still want to be able to do the "are these the same pointer" comparisons. These are usually relied upon for, for instance, inserting an element into a list where no duplicates are allowed, and some of your framework stuff may not work if this operator is overloaded in a non-standard way.
下面我总结了实现 IEquatable 时需要执行的操作,并提供了各个 MSDN 文档页面的理由。
总结
理由
IEquatable
IEquatable.Equals 方法
重写 Equals() 和运算符 == 的指南(C# 编程指南)
== 运算符(C# 参考)
Object.Equals 方法(对象)
其他陷阱
Below I have summed up what you need to do when implementing IEquatable and provided the justification from the various MSDN documentation pages.
Summary
Justification
IEquatable
IEquatable.Equals Method
Guidelines for Overriding Equals() and Operator == (C# Programming Guide)
== Operator (C# Reference)
Object.Equals Method (Object)
Additional Gotchas
微软似乎改变了他们的态度,或者至少存在关于不重载相等运算符的冲突信息。 根据这篇 Microsoft 文章,标题为“How to: Define Value Equality for a类型:
“== 和 != 运算符可以与类一起使用,即使类没有重载它们。但是,默认行为是执行引用相等性检查。在类中,如果重载 Equals 方法,则应该重载 == 和 != 运算符,但这不是必需的。”
根据 Eric Lippert 在他对我提出的有关 回答中的说法questions/32172273/minimal-code-for-equality-in-c-sharp">C# 中平等的最小代码 - 他说:
“这里遇到的危险是你得到了一个定义为 == 的运算符默认情况下引用相等的情况很容易出现这样的情况:重载的 Equals 方法实现值相等,而 == 实现引用相等,然后您意外地对值相等的非引用相等的事物使用了引用相等。这是一种容易出错的做法,很难通过人工代码审查来发现。
几年前,我研究了一种静态分析算法来统计检测这种情况,我们发现每百万行代码中的缺陷率约为两个实例。在我们研究的所有代码库中,当仅考虑在某些地方重写了 Equals 的代码库时,缺陷率显然要高得多!
此外,还要考虑成本与风险。 如果您已经实现了 IComparable,那么编写所有运算符只是简单的一行代码,不会有错误,也永远不会更改。 这是您要编写的最便宜的代码。 如果要在编写和测试十几个小方法的固定成本与查找和修复使用引用相等而不是值相等的难以发现的错误的无限成本之间进行选择,我知道我会选择哪一个。”
.NET Framework 永远不会对您编写的任何类型使用 == 或 != 但是,如果其他人这样做,会发生什么情况,所以,如果该类是针对第三方的,那么我总是会提供 == 或 != 。 == 和 != 运算符。如果该类仅供组内部使用,我仍然可能会实现 == 和 != 运算符,
我只会实现 <、<=、> 和 。 >= 运算符(如果实现了 IComparable)。仅当类型需要支持排序时才应实现 IComparable - 例如在排序或在有序通用容器(如 SortedSet)中使用时,
如果组或公司制定了不实现的策略 。 == 和 != 运算符 - 那么我当然会遵循该策略,如果存在这样的策略,那么明智的做法是使用标记 == 和 的任何出现的 Q/A 代码分析工具来强制执行它。 != 运算符与引用类型一起使用。
Microsoft appears to have changed their tune, or at least there is conflicting info about not overloading the equality operator. According to this Microsoft article titled How to: Define Value Equality for a Type:
"The == and != operators can be used with classes even if the class does not overload them. However, the default behavior is to perform a reference equality check. In a class, if you overload the Equals method, you should overload the == and != operators, but it is not required."
According to Eric Lippert in his answer to a question I asked about Minimal code for equality in C# - he says:
"The danger you run into here is that you get an == operator defined for you that does reference equality by default. You could easily end up in a situation where an overloaded Equals method does value equality and == does reference equality, and then you accidentally use reference equality on not-reference-equal things that are value-equal. This is an error-prone practice that is hard to spot by human code review.
A couple years ago I worked on a static analysis algorithm to statistically detect this situation, and we found a defect rate of about two instances per million lines of code across all codebases we studied. When considering just codebases which had somewhere overridden Equals, the defect rate was obviously considerably higher!
Moreover, consider the costs vs the risks. If you already have implementations of IComparable then writing all the operators is trivial one-liners that will not have bugs and will never be changed. It's the cheapest code you're ever going to write. If given the choice between the fixed cost of writing and testing a dozen tiny methods vs the unbounded cost of finding and fixing a hard-to-see bug where reference equality is used instead of value equality, I know which one I would pick."
The .NET Framework will not ever use == or != with any type that you write. But, the danger is what would happen if someone else does. So, if the class is for a 3rd party, then I would always provide the == and != operators. If the class is only intended to be used internally by the group, I would still probably implement the == and != operators.
I would only implement the <, <=, >, and >= operators if IComparable was implemented. IComparable should only be implemented if the type needs to support ordering - like when sorting or being used in an ordered generic container like SortedSet.
If the group or company had a policy in place to not ever implement the == and != operators - then I would of course follow that policy. If such a policy were in place, then it would be wise to enforce it with a Q/A code analysis tool that flags any occurrence of the == and != operators when used with a reference type.
那篇文章只是建议不要重写相等运算符(对于引用类型),而不是反对重写 Equals。 如果相等检查不仅仅意味着引用检查,那么您应该在对象(引用或值)中覆盖 Equals。 如果您想要一个接口,您还可以实现 IEquatable (由通用集合)。 但是,如果您确实实现了 IEquatable,则还应该覆盖 equals,如 IEquatable 备注部分所述:
关于是否应该实现 Equals 和/或相等运算符:
来自 实现等于方法
来自 实现等于和等于运算符 (==) 的指南
这只是说,每当您实现相等运算符时,您都需要覆盖 Equals。 它确实没有表明您在重写Equals 时需要重写相等运算符。
That article just recommends against overriding the equality operator (for reference types), not against overriding Equals. You should override Equals within your object (reference or value) if equality checks will mean something more than reference checks. If you want an interface, you can also implement IEquatable (used by generic collections). If you do implement IEquatable, however, you should also override equals, as the IEquatable remarks section states:
In regards to whether you should implement Equals and/or the equality operator:
From Implementing the Equals Method
From Guidelines for Implementing Equals and the Equality Operator (==)
This only says that you need to override Equals whenever you implement the equality operator. It does not say that you need to override the equality operator when you override Equals.
对于将产生特定比较的复杂对象,那么实现 IComparable 并在 Compare 方法中定义比较是一个很好的实现。
例如,我们有“车辆”对象,其中唯一的区别可能是注册号,我们使用它进行比较以确保测试中返回的预期值是我们想要的值。
For complex objects that will yield specific comparisons then implementing IComparable and defining the comparison in the Compare methods is a good implementation.
For example we have "Vehicle" objects where the only difference may be the registration number and we use this to compare to ensure that the expected value returned in testing is the one we want.
我倾向于使用 Resharper 自动制作的东西。 例如,它为我的一种引用类型自动创建了这个:
如果您想覆盖
==
并仍然进行引用检查,您仍然可以使用Object.ReferenceEquals
。I tend to use what Resharper automatically makes. for example, it autocreated this for one of my reference types:
If you want to override
==
and still do ref checks, you can still useObject.ReferenceEquals
.我相信,对于 .NET 的设计来说,像检查对象是否相等这样简单的事情有点棘手。
对于结构
1) 实现
IEquatable
。 它显着提高了性能。2) 由于您现在拥有自己的
Equals
,请重写GetHashCode
,并与各种相等性检查重写object.Equals
保持一致。3) 重载
==
和!=
运算符不需要虔诚地完成,因为如果您无意中使用== 或
!=
,但这样做最好与Equals
方法保持一致。对于班级
来自 MS:
对我来说,
==
感觉就像值相等,更像是Equals
方法的语法糖。 编写a == b
比编写a.Equals(b)
直观得多。 我们很少需要检查引用相等性。 在处理物理对象的逻辑表示的抽象级别中,这不是我们需要检查的内容。 我认为==
和Equals
具有不同的语义实际上可能会令人困惑。 我相信首先应该是==
表示值相等,而Equals
表示引用(或更好的名称,如IsSameAs
)。 我不想在这里认真对待 MS 指南,不仅因为它对我来说不自然,而且还因为重载==
不会造成任何重大伤害。这与不覆盖非泛型Equals
或GetHashCode
不同,后者可能会反噬,因为框架不会在任何地方使用==
,除非我们自己使用用它。 我从不重载==
和!=
中获得的唯一真正好处是与整个框架设计的一致性,而我没有控制。 这确实是一件大事,所以遗憾的是我会坚持下去。使用引用语义(可变对象)
1) 覆盖
Equals
和GetHashCode
。2) 实现
IEquatable
不是必须的,但如果你有一个那就太好了。具有值语义(不可变对象)
这是棘手的部分。 如果不小心,很容易搞砸。
1) 覆盖
Equals
和GetHashCode
。2) 重载
==
和!=
以匹配Equals
。 确保它适用于 null。2) 实现
IEquatable
不是必须的,但如果你有一个那就太好了。要特别注意如果您的类可以继承,情况会如何,在这种情况下,您必须确定基类对象是否可以等于派生类对象。 理想情况下,如果没有派生类的对象用于相等性检查,则基类实例可以等于派生类实例,在这种情况下,不需要检查泛型
Type
相等性。 code>Equals 基类。一般来说,请注意不要重复代码。 我本可以制作一个通用抽象基类(
IEqualized
左右)作为模板,以便更轻松地重用,但遗憾的是在 C# 中,这阻止了我从其他类派生。I believe getting something as simple as checking objects for equality correct is a bit tricky with .NET's design.
For Struct
1) Implement
IEquatable<T>
. It improves performance noticeably.2) Since you're having your own
Equals
now, overrideGetHashCode
, and to be consistent with various equality checking overrideobject.Equals
as well.3) Overloading
==
and!=
operators need not be religiously done since the compiler will warn if you unintentionally equate a struct with another with a==
or!=
, but its good to do so to be consistent withEquals
methods.For Class
From MS:
To me
==
feels like value equality, more like a syntactic sugar forEquals
method. Writinga == b
is much more intuitive than writinga.Equals(b)
. Rarely we'll need to check reference equality. In abstract levels dealing with logical representations of physical objects this is not something we would need to check. I think having different semantics for==
andEquals
can actually be confusing. I believe it should have been==
for value equality andEquals
for reference (or a better name likeIsSameAs
) equality in the first place. I would love to not take MS guideline seriously here, not just because it isn't natural to me, but also because overloading==
doesn't do any major harm. That's unlike not overriding non-genericEquals
orGetHashCode
which can bite back, because framework doesn't use==
anywhere but only if we ourself use it. The only real benefit I gain from not overloading==
and!=
will be the consistency with design of the entire framework over which I have no control of. And that's indeed a big thing, so sadly I will stick to it.With reference semantics (mutable objects)
1) Override
Equals
andGetHashCode
.2) Implementing
IEquatable<T>
isn't a must, but will be nice if you have one.With value semantics (immutable objects)
This is the tricky part. Can get easily messed up if not taken care..
1) Override
Equals
andGetHashCode
.2) Overload
==
and!=
to matchEquals
. Make sure it works for nulls.2) Implementing
IEquatable<T>
isn't a must, but will be nice if you have one.Take special care to see how it should fare if your class can be inherited, in such cases you will have to determine if a base class object can be equal to a derived class object. Ideally, if no objects of derived class is used for equality checking, then a base class instance can be equal to a derived class instance and in such cases, there is no need to check
Type
equality in genericEquals
of base class.In general take care not to duplicate code. I could have made a generic abstract base class (
IEqualizable<T>
or so) as a template to allow re-use easier, but sadly in C# that stops me from deriving from additional classes.我在这个
例子中使用
IEqualityComparer
或IEquatable
:产品实体:
产品的平等比较器
通过 LinQ 使用此功能,
您也可以使用重载运算符,例如:
这可以是使用更干净的方式实现:
I use
IEqualityComparer
orIEquatable
for thisexample:
Product Entity:
Equality Comparer for Product
Use this via LinQ
you may use overloaded operators for this as well e.g. something like :
this can be achieved using a more clean way :
上面的所有答案都不考虑多态性,通常您希望派生引用使用派生的 Equals,即使通过基本引用进行比较也是如此。 请在此处查看问题/讨论/答案 - 平等和多态性
All the answers above do not consider polymorphism, often you want derived references to use the derived Equals even when compared via a base reference. Please see the question/ discussion/ answers here - Equality and polymorphism