C# 比较引用类型的两个实例的“最佳实践”是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/104158/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is "Best Practice" For Comparing Two Instances of a Reference Type?
提问by Rob Cooper
I came across this recently, up until now I have been happily overriding the equality operator (==) and/or Equalsmethod in order to see if two references types actually contained the same data(i.e. two different instances that look the same).
我最近遇到了这个问题,直到现在我一直很高兴地覆盖相等运算符 ( ==) 和/或Equals方法,以查看两个引用类型是否实际上包含相同的数据(即两个看起来相同的不同实例)。
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 MSDNI came across an articlethat advises against it. Now I understand whythe article is saying this (because they are not the same instance) but it does not answer the question:
在查看MSDN 中的一些编码标准指南时,我发现了一篇反对它的文章。现在我明白为什么这篇文章会这么说(因为它们不是同一个实例),但它没有回答这个问题:
- 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?!
- 比较两种引用类型的最佳方法是什么?
- 我们应该实现IComparable吗?(我也看到提到这应该只保留给值类型)。
- 有什么我不知道的接口吗?
- 我们应该自己动手吗?!
Many Thanks ^_^
非常感谢^_^
Update
更新
Looks like I had mis-read some of the documentation (it's been a long day) and overriding Equalsmay be the way to go..
看起来我误读了一些文档(这是漫长的一天)并且覆盖Equals可能是要走的路..
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 equalityoperator, 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.
如果您正在实现引用类型,并且您的类型看起来像一个基类型,例如 Point、String、BigNumber 等,则您应该考虑覆盖引用类型上的 Equals 方法。大多数引用类型不应重载相等运算符,即使它们覆盖了 Equals。但是,如果您正在实现旨在具有值语义的引用类型,例如复数类型,则应覆盖相等运算符。
采纳答案by Matt J
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?".
看起来您正在用 C# 编码,它有一个名为 Equals 的方法,您的类应该实现该方法,如果您想使用其他度量来比较两个对象而不是“这两个指针(因为对象句柄就是指针)到相同的内存地址?”。
I grabbed some sample code from here:
我从这里获取了一些示例代码:
class TwoDPoint : System.Object
{
public readonly int x, y;
public TwoDPoint(int x, int y) //constructor
{
this.x = x;
this.y = y;
}
public override bool Equals(System.Object obj)
{
// If parameter is null return false.
if (obj == null)
{
return false;
}
// If parameter cannot be cast to Point return false.
TwoDPoint p = obj as TwoDPoint;
if ((System.Object)p == null)
{
return false;
}
// Return true if the fields match:
return (x == p.x) && (y == p.y);
}
public bool Equals(TwoDPoint p)
{
// If parameter is null return false:
if ((object)p == null)
{
return false;
}
// Return true if the fields match:
return (x == p.x) && (y == p.y);
}
public override int GetHashCode()
{
return x ^ y;
}
}
Java has very similar mechanisms. The equals()method is part of the Objectclass, and your class overloads it if you want this type of functionality.
Java 具有非常相似的机制。该的equals()方法是一部分对象类和类重载,如果你想这种类型的功能。
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.
重载 '==' 对对象来说可能是一个坏主意的原因是,通常,您仍然希望能够进行“这些指针是否相同”的比较。这些通常用于,例如,将元素插入到不允许重复的列表中,如果此运算符以非标准方式重载,则您的某些框架内容可能无法正常工作。
回答by Paul Shannon
For complex objects that will yield specific comparisons then implementing IComparable and defining the comparison in the Compare methods is a good implementation.
对于将产生特定比较的复杂对象,实现 IComparable 并在 Compare 方法中定义比较是一个很好的实现。
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.
例如,我们有“车辆”对象,其中唯一的区别可能是注册号,我们使用它来比较以确保测试中返回的预期值是我们想要的。
回答by bdukes
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:
那篇文章只是建议不要覆盖相等运算符(对于引用类型),而不是反对覆盖 Equals。如果相等性检查的意义不仅仅是引用检查,则您应该在对象(引用或值)中覆盖 Equals。如果你想要一个接口,你也可以实现IEquatable(被泛型集合使用)。但是,如果您确实实现了 IEquatable,您还应该覆盖 equals,如 IEquatable 备注部分所述:
If you implement IEquatable<T>, you should also override the base class implementations of Object.Equals(Object) and GetHashCode so that their behavior is consistent with that of the IEquatable<T>.Equals method. If you do override Object.Equals(Object), your overridden implementation is also called in calls to the static Equals(System.Object, System.Object) method on your class. This ensures that all invocations of the Equals method return consistent results.
如果您实现 IEquatable<T>,您还应该覆盖 Object.Equals(Object) 和 GetHashCode 的基类实现,以便它们的行为与 IEquatable<T>.Equals 方法的行为一致。如果您确实覆盖了 Object.Equals(Object),则您的覆盖实现也会在对类上的静态 Equals(System.Object, System.Object) 方法的调用中调用。这可确保 Equals 方法的所有调用返回一致的结果。
In regards to whether you should implement Equals and/or the equality operator:
关于是否应该实现 Equals 和/或相等运算符:
From Implementing the Equals Method
Most reference types should not overload the equality operator, even if they override Equals.
大多数引用类型不应重载相等运算符,即使它们覆盖了 Equals。
From Guidelines for Implementing Equals and the Equality Operator (==)
Override the Equals method whenever you implement the equality operator (==), and make them do the same thing.
每当您实现相等运算符 (==) 时,重写 Equals 方法,并使它们执行相同的操作。
This only says that you need to override Equals whenever you implement the equality operator. It does notsay that you need to override the equality operator when you override Equals.
这只是说明每当您实现相等运算符时都需要覆盖 Equals。它并不是说在覆盖 Equals 时需要覆盖相等运算符。
回答by mattlant
I tend to use what Resharper automatically makes. for example, it autocreated this for one of my reference types:
我倾向于使用 Resharper 自动制作的东西。例如,它为我的一种引用类型自动创建了这个:
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
return obj.GetType() == typeof(SecurableResourcePermission) && Equals((SecurableResourcePermission)obj);
}
public bool Equals(SecurableResourcePermission obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
return obj.ResourceUid == ResourceUid && Equals(obj.ActionCode, ActionCode) && Equals(obj.AllowDeny, AllowDeny);
}
public override int GetHashCode()
{
unchecked
{
int result = (int)ResourceUid;
result = (result * 397) ^ (ActionCode != null ? ActionCode.GetHashCode() : 0);
result = (result * 397) ^ AllowDeny.GetHashCode();
return result;
}
}
If you want to override ==
and still do ref checks, you can still use Object.ReferenceEquals
.
如果您想覆盖==
并仍然进行 ref 检查,您仍然可以使用Object.ReferenceEquals
.
回答by Konrad Rudolph
Implementing equality in .NET correctly, efficiently and without code duplicationis 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 ==
, !=
).
在 .NET 中正确、有效地实现相等性并且没有代码重复是很困难的。具体而言,值语义的引用类型(即不可变类型的治疗equvialence作为平等的),你应该实现的System.IEquatable<T>
接口,你应该实现所有不同的操作(Equals
,GetHashCode
和==
,!=
)。
As an example, here's a class implementing value equality:
例如,这是一个实现值相等的类:
class Point : IEquatable<Point> {
public int X { get; }
public int Y { get; }
public Point(int x = 0, int y = 0) { X = x; Y = y; }
public bool Equals(Point other) {
if (other is null) return false;
return X.Equals(other.X) && Y.Equals(other.Y);
}
public override bool Equals(object obj) => Equals(obj as Point);
public static bool operator ==(Point lhs, Point rhs) => object.Equals(lhs, rhs);
public static bool operator !=(Point lhs, Point rhs) => ! (lhs == rhs);
public override int GetHashCode() => X.GetHashCode() ^ Y.GetHashCode();
}
The only movable parts in the above code are the bolded parts: the second line in Equals(Point other)
and the GetHashCode()
method. The other code should remain unchanged.
上面代码中唯一可移动的部分是粗体部分:第二行Equals(Point other)
和GetHashCode()
方法。其他代码应保持不变。
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 intentionallyequates 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)
and Equals(SecurableResourcePermission x)
willtreat this case differently.
该代码有意将甚至派生类类型的对象等同起来。通常,这可能是不可取的,因为基类和派生类之间的相等性没有明确定义。不幸的是,.NET 和编码指南在这里不是很清楚。Resharper 创建的代码发布在另一个答案中,在这种情况下很容易受到不良行为的影响,因为Equals(object x)
并且Equals(SecurableResourcePermission x)
会以不同的方式对待这种情况。
In order to change this behaviour, an additional type check has to be inserted in the strongly-typed Equals
method above:
为了改变这种行为,必须在Equals
上面的强类型方法中插入额外的类型检查:
public bool Equals(Point other) {
if (other is null) return false;
if (other.GetType() != GetType()) return false;
return X.Equals(other.X) && Y.Equals(other.Y);
}
回答by Zach Burlingame
Below I have summed up what you need to do when implementing IEquatable and provided the justification from the various MSDN documentation pages.
下面我总结了您在实现 IEquatable 时需要做的事情,并提供了来自各种 MSDN 文档页面的理由。
Summary
概括
- When testing for value equality is desired (such as when using objects in collections) you should implement the IEquatable interface, override Object.Equals, and GetHashCode for your class.
- When testing for reference equality is desired you should use operator==,operator!= and Object.ReferenceEquals.
- You should only override operator== and operator!= for ValueTypesand immutable reference types.
- 当需要测试值相等时(例如在集合中使用对象时),您应该为您的类实现 IEquatable 接口、覆盖 Object.Equals 和 GetHashCode。
- 当需要测试引用相等性时,您应该使用 operator==,operator!= 和Object.ReferenceEquals。
- 对于ValueType和不可变引用类型,您应该只覆盖 operator== 和 operator!= 。
Justification
理由
The System.IEquatable interface is used to compare two instances of an object for equality. The objects are compared based on the logic implemented in the class. The comparison results in a boolean value indicating if the objects are different. This is in contrast to the System.IComparable interface, which return an integer indicating how the object values are different.
The IEquatable interface declares two methods that must be overridden. The Equals method contains the implementation to perform the actual comparison and return true if the object values are equal, or false if they are not. The GetHashCode method should return a unique hash value that may be used to uniquely identify identical objects that contain different values. The type of hashing algorithm used is implementation-specific.
System.IEquatable 接口用于比较对象的两个实例是否相等。根据类中实现的逻辑比较对象。比较结果是一个布尔值,指示对象是否不同。这与 System.IComparable 接口形成对比,后者返回一个整数,指示对象值的不同之处。
IEquatable 接口声明了两个必须重写的方法。Equals 方法包含执行实际比较的实现,如果对象值相等则返回 true,否则返回 false。GetHashCode 方法应返回一个唯一的哈希值,该值可用于唯一标识包含不同值的相同对象。使用的散列算法类型是特定于实现的。
- You should implement IEquatable for your objects to handle the possibility that they will be stored in an array or generic collection.
- If you implement IEquatable you should also override the base class implementations of Object.Equals(Object) and GetHashCode so that their behavior is consistent with that of the IEquatable.Equals method
- 您应该为您的对象实现 IEquatable 以处理它们存储在数组或泛型集合中的可能性。
- 如果您实现 IEquatable,您还应该覆盖 Object.Equals(Object) 和 GetHashCode 的基类实现,以便它们的行为与 IEquatable.Equals 方法的行为一致
Guidelines for Overriding Equals() and Operator == (C# Programming Guide)
覆盖 Equals() 和 Operator == 的准则(C# 编程指南)
- x.Equals(x) returns true.
- x.Equals(y) returns the same value as y.Equals(x)
- if (x.Equals(y) && y.Equals(z)) returns true, then x.Equals(z) returns true.
- Successive invocations of x. Equals (y) return the same value as long as the objects referenced by x and y are not modified.
- x. Equals (null) returns false (for non-nullable value types only. For more information, see Nullable Types (C# Programming Guide).)
- The new implementation of Equals should not throw exceptions.
- It is recommended that any class that overrides Equals also override Object.GetHashCode.
- Is is recommended that in addition to implementing Equals(object), any class also implement Equals(type) for their own type, to enhance performance.
By default, the operator == tests for reference equality by determining whether two references indicate the same object.Therefore, reference types do not have to implement operator == in order to gain this functionality. When a type is immutable, that is, the data that is contained in the instance cannot be changed, overloading operator == to compare value equality instead of reference equality can be useful because, as immutable objects, they can be considered the same as long as they have the same value. It is not a good idea to override operator == in non-immutable types.
- Overloaded operator == implementations should not throw exceptions.
- Any type that overloads operator == should also overload operator !=.
- x.Equals(x) 返回真。
- x.Equals(y) 返回与 y.Equals(x) 相同的值
- 如果 (x.Equals(y) && y.Equals(z)) 返回真,则 x.Equals(z) 返回真。
- x 的连续调用。只要 x 和 y 引用的对象没有被修改,等于 (y) 就会返回相同的值。
- X。Equals (null) 返回 false(仅适用于不可为 null 的值类型。有关详细信息,请参阅Nullable 类型(C# 编程指南)。)
- Equals 的新实现不应该抛出异常。
- 建议覆盖 Equals 的任何类也覆盖 Object.GetHashCode。
- 建议除了实现 Equals(object) 之外,任何类还为自己的类型实现 Equals(type),以提高性能。
默认情况下,运算符 == 通过确定两个引用是否指示同一个对象来测试引用相等性。因此,引用类型不必为了获得此功能而实现运算符 ==。当类型是不可变的,即实例中包含的数据不能改变时,重载运算符 == 来比较值相等而不是引用相等会很有用,因为作为不可变对象,它们可以被认为与 long 相同因为它们具有相同的值。在非不可变类型中覆盖运算符 == 不是一个好主意。
- 重载的 operator == 实现不应抛出异常。
- 任何重载 operator == 的类型也应该重载 operator !=。
- For predefined value types, the equality operator (==) returns true if the values of its operands are equal, false otherwise.
- For reference types other than string, == returns true if its two operands refer to the same object.
- For the string type, == compares the values of the strings.
- When testing for null using == comparisons within your operator== overrides, make sure you use the base object class operator. If you don't, infinite recursion will occur resulting in a stackoverflow.
- 对于预定义的值类型,如果操作数的值相等,则相等运算符 (==) 返回 true,否则返回 false。
- 对于字符串以外的引用类型,如果 == 的两个操作数引用同一个对象,则返回 true。
- 对于字符串类型,== 比较字符串的值。
- 在 operator== 覆盖中使用 == 比较测试 null 时,请确保使用基对象类运算符。如果不这样做,将发生无限递归,从而导致堆栈溢出。
If your programming language supports operator overloading and if you choose to overload the equality operator for a given type, that type must override the Equals method. Such implementations of the Equals method must return the same results as the equality operator
The following guidelines are for implementing a value type:
- Consider overriding Equals to gain increased performance over that provided by the default implementation of Equals on ValueType.
- If you override Equals and the language supports operator overloading, you must overload the equality operator for your value type.
The following guidelines are for implementing a reference type:
- Consider overriding Equals on a reference type if the semantics of the type are based on the fact that the type represents some value(s).
- Most reference types must 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 must override the equality operator.
如果您的编程语言支持运算符重载,并且您选择为给定类型重载相等运算符,则该类型必须覆盖 Equals 方法。Equals 方法的此类实现必须返回与相等运算符相同的结果
以下准则用于实现值类型:
- 考虑覆盖 Equals 以获得比 ValueType 上 Equals 的默认实现所提供的更高的性能。
- 如果覆盖 Equals 并且语言支持运算符重载,则必须为您的值类型重载相等运算符。
以下准则用于实现引用类型:
- 如果类型的语义基于类型表示某些值的事实,请考虑覆盖引用类型上的 Equals。
- 大多数引用类型不得重载相等运算符,即使它们覆盖了 Equals。但是,如果您要实现旨在具有值语义的引用类型,例如复数类型,则必须覆盖相等运算符。
Additional Gotchas
额外的陷阱
- When overriding GetHashCode() make sure you test reference types for NULL before using them in the hash code.
- I ran into a problem with interface-based programming and operator overloading described here: Operator Overloading with Interface-Based Programming in C#
- 覆盖 GetHashCode() 时,请确保在哈希代码中使用引用类型之前测试 NULL 的引用类型。
- 我遇到了此处描述的基于接口的编程和运算符重载的问题:运算符重载与 C# 中的基于接口的编程
回答by nawfal
I believe getting something as simple as checking objects for equality correct is a bit tricky with .NET's design.
我相信像检查对象是否相等这样简单的事情对于 .NET 的设计来说有点棘手。
For Struct
对于结构
1) Implement IEquatable<T>
. It improves performance noticeably.
1) 实施IEquatable<T>
。它显着提高了性能。
2) Since you're having your own Equals
now, override GetHashCode
, and to be consistent with various equality checking override object.Equals
as well.
2)由于您现在拥有自己的Equals
覆盖GetHashCode
,并且与各种相等性检查覆盖保持一致object.Equals
。
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 with Equals
methods.
3) 重载==
和!=
运算符不需要认真地完成,因为如果您无意中将一个结构体与另一个结构体等同于一个==
or !=
,编译器会发出警告,但这样做最好与Equals
方法保持一致。
public struct Entity : IEquatable<Entity>
{
public bool Equals(Entity other)
{
throw new NotImplementedException("Your equality check here...");
}
public override bool Equals(object obj)
{
if (obj == null || !(obj is Entity))
return false;
return Equals((Entity)obj);
}
public static bool operator ==(Entity e1, Entity e2)
{
return e1.Equals(e2);
}
public static bool operator !=(Entity e1, Entity e2)
{
return !(e1 == e2);
}
public override int GetHashCode()
{
throw new NotImplementedException("Your lightweight hashing algorithm, consistent with Equals method, here...");
}
}
For Class
上课
From MS:
来自 MS:
Most reference types should not overload the equality operator, even if they override Equals.
大多数引用类型不应重载相等运算符,即使它们覆盖了 Equals。
To me ==
feels like value equality, more like a syntactic sugar for Equals
method. Writing a == b
is much more intuitive than writing a.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 ==
and Equals
can actually be confusing. I believe it should have been ==
for value equality and Equals
for reference (or a better name like IsSameAs
) 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-generic Equals
or GetHashCode
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.
对我来说==
感觉就像价值平等,更像是Equals
方法的语法糖。写作a == b
比写作更直观a.Equals(b)
。我们很少需要检查引用相等性。在处理物理对象的逻辑表示的抽象层次中,这不是我们需要检查的。我认为有不同的语义==
和Equals
实际上可以混淆。我相信它首先应该是==
为了价值平等和Equals
参考(或更好的名字IsSameAs
)平等。我不想在这里认真对待 MS 指南,不仅因为它对我来说不自然,而且因为超载==
不会造成任何重大伤害。这不像不覆盖非泛型Equals
或者GetHashCode
哪个可以反击,因为框架不会==
在任何地方使用,而只有我们自己使用它。我从获得的唯一的真正的好处不超载==
,并!=
会与整个框架的设计一致性过,我没有控制权。这确实是一件大事,所以很遗憾我会坚持下去。
With reference semantics (mutable objects)
带引用语义(可变对象)
1) Override Equals
and GetHashCode
.
1)覆盖Equals
和GetHashCode
。
2) Implementing IEquatable<T>
isn't a must, but will be nice if you have one.
2)实施IEquatable<T>
不是必须的,但如果你有的话会很好。
public class Entity : IEquatable<Entity>
{
public bool Equals(Entity other)
{
if (ReferenceEquals(this, other))
return true;
if (ReferenceEquals(null, other))
return false;
//if your below implementation will involve objects of derived classes, then do a
//GetType == other.GetType comparison
throw new NotImplementedException("Your equality check here...");
}
public override bool Equals(object obj)
{
return Equals(obj as Entity);
}
public override int GetHashCode()
{
throw new NotImplementedException("Your lightweight hashing algorithm, consistent with Equals method, here...");
}
}
With value semantics (immutable objects)
带值语义(不可变对象)
This is the tricky part. Can get easily messed up if not taken care..
这是棘手的部分。如果不小心很容易搞砸..
1) Override Equals
and GetHashCode
.
1)覆盖Equals
和GetHashCode
。
2) Overload ==
and !=
to match Equals
. Make sure it works for nulls.
2)重载==
和!=
匹配Equals
。确保它适用于 nulls。
2) Implementing IEquatable<T>
isn't a must, but will be nice if you have one.
2)实施IEquatable<T>
不是必须的,但如果你有的话会很好。
public class Entity : IEquatable<Entity>
{
public bool Equals(Entity other)
{
if (ReferenceEquals(this, other))
return true;
if (ReferenceEquals(null, other))
return false;
//if your below implementation will involve objects of derived classes, then do a
//GetType == other.GetType comparison
throw new NotImplementedException("Your equality check here...");
}
public override bool Equals(object obj)
{
return Equals(obj as Entity);
}
public static bool operator ==(Entity e1, Entity e2)
{
if (ReferenceEquals(e1, null))
return ReferenceEquals(e2, null);
return e1.Equals(e2);
}
public static bool operator !=(Entity e1, Entity e2)
{
return !(e1 == e2);
}
public override int GetHashCode()
{
throw new NotImplementedException("Your lightweight hashing algorithm, consistent with Equals method, here...");
}
}
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 generic Equals
of base class.
如果您的类可以被继承,请特别注意它应该如何处理,在这种情况下,您必须确定基类对象是否可以等于派生类对象。理想情况下,如果没有派生类的对象用于相等性检查,那么基类实例可以等于派生类实例,在这种情况下,不需要检查基类Type
泛型中Equals
的相等性。
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.
通常注意不要重复代码。我本可以将通用抽象基类(IEqualizable<T>
左右)作为模板,以便更轻松地重用,但遗憾的是,在 C# 中,这阻止了我从其他类派生。
回答by Bob Bryan
Microsoft appears to have changed their tune, or at least there is conflicting info about not overloading the equality operator. According to this Microsoft articletitled How to: Define Value Equality for a Type:
微软似乎已经改变了他们的调子,或者至少存在关于不重载相等运算符的冲突信息。根据这篇题为如何:为类型定义值相等的Microsoft 文章:
"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."
“== 和 != 运算符可以与类一起使用,即使类没有重载它们。但是,默认行为是执行引用相等性检查。在类中,如果重载 Equals 方法,则应该重载== 和 != 运算符,但这不是必需的。”
According to Eric Lippert in his answerto a question I asked about Minimal code for equality in C#- he says:
根据埃里克·利珀特 (Eric Lippert) 在回答我问的有关C# 中平等的最小代码的问题的回答- 他说:
"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.
“你在这里遇到的危险是你为你定义了一个 == 运算符,默认情况下它执行引用相等。你很容易陷入这样一种情况,即重载的 Equals 方法执行值相等而 == 执行引用相等,然后你不小心在值相等的非引用相等事物上使用引用相等。这是一种容易出错的做法,人工代码审查很难发现。
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!
几年前,我研究了一种静态分析算法来统计检测这种情况,我们发现在我们研究的所有代码库中,每百万行代码中大约有两个实例的缺陷率。当仅考虑在某处覆盖了 Equals 的代码库时,缺陷率显然要高得多!
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."
此外,请考虑成本与风险。如果您已经有了 IComparable 的实现,那么编写所有的操作符只是简单的单行代码,不会有错误并且永远不会改变。这是您将要编写的最便宜的代码。如果在编写和测试十几种小方法的固定成本与查找和修复使用引用相等而不是值相等的难以看到的错误的无限成本之间做出选择,我知道我会选择哪个。”
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.
.NET Framework 永远不会将 == 或 != 用于您编写的任何类型。但是,危险是如果其他人这样做会发生什么。因此,如果该课程适用于第 3 方,那么我将始终提供 == 和 != 运算符。如果该类仅打算由组内部使用,我仍然可能会实现 == 和 != 运算符。
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.
如果实现了 IComparable,我将只实现 <、<=、> 和 >= 运算符。IComparable 应该只在类型需要支持排序时实现 - 比如排序或在有序的通用容器中使用,如 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.
如果团体或公司制定了不实施 == 和 != 运算符的政策,那么我当然会遵循该政策。如果这样的策略到位,那么使用 Q/A 代码分析工具强制执行它是明智的,该工具在与引用类型一起使用时标记 == 和 != 运算符的任何出现。
回答by kofifus
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
上面的所有答案都没有考虑多态性,通常您希望派生引用使用派生的 Equals,即使在通过基本引用进行比较时也是如此。请在此处查看问题/讨论/答案 -平等和多态性