简体   繁体   English

Object.Equals:默认情况下一切都是相同的

[英]Object.Equals: everything is equal by default

While reading Jeffrey Richter's CLR via C# 4th edition (Microsoft Press), the author at one point states that while Object.Equals currently checks for identity equality, Microsoft should have implemented the method like this : 通过C#第4版 (微软出版社)阅读Jeffrey Richter的CLR时 ,作者曾指出,虽然Object.Equals目前检查身份相同,但Microsoft 应该实现如下方法

public class Object {
    public virtual Boolean Equals(Object obj) {
        // The given object to compare to can't be null
        if (obj == null) return false;

        // If objects are different types, they can't be equal.
        if (this.GetType() != obj.GetType()) return false;

        // If objects are same type, return true if all of their fields match
        // Because System.Object defines no fields, the fields match
        return true;
    }
}

This strikes me as very odd: every non-null object of the same type would be equal by default? 这让我很奇怪:默认情况下,相同类型的每个非null对象都是相同的 So unless overridden: all instances of a type are equal (eg all your locking objects are equal), and return the same hash code. 因此,除非被覆盖:类型的所有实例都相等(例如,所有锁定对象都相等),并返回相同的哈希码。 And assuming that the == on Object still checks reference equality, this would mean that (a == b) != a.Equals(b) which would also be strange. 并且假设== on Object仍然检查引用相等,这意味着(a == b) != a.Equals(b)也会很奇怪。

I think the idea of things being equal if it is the exact same thing (identity) is a better idea than just making everything equal unless overridden. 我认为事物是平等的,如果它是完全相同的(身份)是一个更好的想法,而不仅仅是让一切都平等,除非被覆盖。 But this is a well known book's 4th edition published by Microsoft, so there must be some merit to this idea. 但这是微软出版的一本着名的第四版,所以这个想法必定有一些优点。 I read the rest of the text but could not help but wonder: Why would the author suggest this? 我阅读了文本的其余部分,但不禁怀疑: 为什么作者会这样做呢? What am I missing here? 我在这里错过了什么? What is the great advantage of Richter's implementation over the current Object.Equals implementation? Richter的实现优于当前的Object.Equals实现的巨大优势是什么?

The current default Equals() does what is known as a shallow compare (or reference compare), and then doesn't check any further if the references differ. 当前默认的Equals()执行所谓的浅比较(或引用比较),然后如果引用不同则不再检查。

I think this is perfectly acceptable for a base implementation . 我认为这对于基础实现是完全可以接受 I certainly wouldn't think that it is wrong or incomplete. 我当然不会认为这是错误的或不完整的。

Richter's example 1 which you quote is also perfectly legitimate for the base System.Object . 您引用的Richter的示例1 对于基本System.Object也是完全合法 The issue with his implementation is that it arguably should be declared abstract 2 - with his method you will end up with an unreliable Equals() on derived objects if you do not override it (because Equals() is supposed to do a deep compare). 他的实现的问题是它可以说应该被声明为抽象2 - 如果你没有覆盖它,那么你将在派生对象上最终得到一个不可靠的Equals() (因为Equals()应该进行深度比较) 。 Having to override this method on all derived objects would be a lot of work, therefore the Microsoft way is better as a default . 必须在所有派生对象上覆盖此方法将会有很多工作,因此Microsoft方法作为默认方式更好。 So in essense you are correct: Richter's example is odd - it is better to default to not equal rather then the other way round (defaulting to true would lead to some rather interesting behavior if people forgot to override it). 所以在本质上你是正确的:里希特的例子是奇怪的 - 最好默认为不相等而不是反过来(如果人们忘记覆盖它,默认为true会导致一些相当有趣的行为)。

(Just for easy reference, here is the default implementation as published in the book) (仅供参考,这是本书中公布的默认实现)

在此输入图像描述



1: Richter is a smart man who knows his stuff and I wouldn't generally argue with anything he says. 1:里希特是一个聪明的人,他知道他的东西,我一般不会与他说的任何事情争论。 You have to understand that the MS engineers would have had to think long and hard about a lot of things, knowing that they didn't have the flexibility of being able to get it wrong and then just fix stuff later. 你必须明白,MS工程师必须长时间地思考很多事情,因为他们知道他们没有灵活性能够弄错,然后才能解决问题。 No matter how right they are, people will always second guess them at a later date, and offer alternative opinions. 无论他们多么正确,人们总会在以后再次猜测它们,并提供其他意见。 That doesn't mean the original is wrong or the alternative is wrong - it simply means there was an alternative. 这并不意味着原始错误或替代方案是错误的 - 它只是意味着有另一种选择。

2: Which of course means that there would be no base implementation, which is good because it would have been unreliable. 2:当然这意味着没有基本实现,这很好,因为它本身是不可靠的。

Jeffery Richter is talking about Value equality over Identity equality. Jeffery Richter谈论的是身份平等的价值平等。

Specifically you ask: 具体你问:

So unless overridden: all instances of a type are equal? 所以除非被覆盖:所有类型的实例都是相同的?

The answer is Yes , But... As in, Yes, But it is (almost) always supposed to be overridden. 答案是肯定的但是......如同,是的, 它(几乎)总是应该被覆盖。

Thus, for most Classes it should be overridden to do a attribute-by-attribute comparison to determine equality. 因此,对于大多数类,应该重写它以进行逐个属性比较以确定相等性。 For some other classes that are truly identity-based (like locks) it should be overridden to use the same technique as it uses today. 对于其他一些真正基于身份的类(如锁),应该重写它以使用与现在相同的技术。

The key though is that it must be overridden in almost every case, and this alone is sufficiently difficult, clumsy and mistake-prone that it is probably why Microsoft did not use this approach. 关键是它几乎在每种情况下都必须被覆盖,而这仅仅是足够困难,笨拙和容易出错,这可能是微软没有使用这种方法的原因。


What is the advantage of Value-Equality over Identity-Equality? 价值平等优于身份平等的优势是什么? It's that if two different objects have the same values/contents, then they can be considered "equal" for purposes of comparison in cases like the Keys of a Dictionary object. 这是因为如果两个不同的对象具有相同的值/内容,那么在诸如Dictionary对象的Keys之类的情况下,它们可以被认为是“相等的”。

Or consider the matter of strings in .Net, which are actually objects, but get treated a lot like values at higher-levels (especially in VB.net). 或者考虑.Net中的字符串问题,它们实际上是对象,但在较高级别(特别是在VB.net中)得到的处理很像。 This presents a problem when you want to compare two strings for equality, because 99% of the time you really do not care if they are different object instances , you only really care if they contain the same text. 当你想比较两个字符串是否相等时会出现问题,因为99%的时候你真的不在乎它们是不同的对象实例 ,你只关心它们是否包含相同的文本。 So .Net has to make sure that that is how string comparison actually works, even though they are really objects. 所以.Net必须确保字符串比较实际上是这样的,即使它们确实是对象。

If one is asked to make a list of all identifiably-distinct objects of arbitrary types, and is not given any indication of what the objects are or what they will be used for, the only universally-applicable means of testing whether two references should be considered as pointing to identifiably-distinct objects is Object.Equals(Object) . 如果要求一个人列出任意类型的所有可识别的不同对象,并且没有给出任何关于对象是什么或它们将被用于什么的指示,那么唯一普遍适用的测试方法是测试两个引用是否应该是被认为是指向可识别的不同对象的是Object.Equals(Object) Two references X and Y should be considered identifiably-distinct if changing one or more references that presently point to X so that they instead point to Y would likely alter program behavior. 如果改变一个或多个当前指向X引用,那么它们应该被认为是可识别的两个引用XY ,以便它们指向Y可能会改变程序行为。

For example, if two instances of string both contain the entire text of War and Peace , punctuated and formatted identically, one could likely replace some or all references to the first with references to the second, or vice versa, with little or no effect on program execution beyond the fact that a comparison between two references which point to the same instance may be found to hold identical text much more quickly than could two references which point to different strings that contain identical characters. 例如,如果两个string实例都包含War和Peace的整个文本,标点和格式相同,则可能会替换对第一个的一些或所有引用,引用第二个,反之亦然,对其影响很小或没有影响。程序执行超出了这样一个事实,即指向同一实例的两个引用之间的比较可能比两个引用指向包含相同字符的不同字符串的引用更快地保存相同的文本。

In most cases, objects which exist to hold immutable data should be considered to be identical if the data they hold is identical. 在大多数情况下,如果保存不可变数据的对象相同,则应认为它们是相同的。 Objects which exist to hold mutable data, or which exist to serve as identity tokens, should generally be considered distinct from each other. 存在用于保存可变数据或存在用作身份令牌的对象通常应被视为彼此不同。 Given that one can define a custom EqualityComparer which will regard as equivalent objects which are not totally equivalent (eg a case-insensitive string comparer), and given that code which needs some definition of equivalence which is broader than strict equivalence should generally know what types it is working with and what definition of equivalence is suitable, it is generally better to have Object.Equals report objects as being different unless they are designed to be substitutable (as would be, eg, strings). 鉴于可以定义一个自定义EqualityComparer ,它将被视为不完全等价的等效对象(例如,不区分大小写的字符串比较器),并且假定需要某些定义等于宽度比严格等价的代码通常应该知道什么类型它正在使用并且等价的定义是合适的,通常最好将Object.Equals报告对象视为不同,除非它们被设计为可替换的(例如,字符串)。

To use a real-world analogy, suppose one is given two pieces of paper, each with a Vehicle Identification Number written on it, and is asked if the car identified by the first piece of paper is the same as the car identified by the second. 为了使用真实世界的类比,假设给出一张纸,每张纸上写有车辆识别号,并询问第一张纸识别的车是否与第二张纸识别的车相同。 。 If the two slips of paper have the same VIN, then clearly the car identified by the first is the same as the one identified by the second. 如果两张纸的VIN具有相同的VIN,则第一张识别的汽车显然与第二张识别的汽车相同。 If they have different VINs, however, excluding any weird possibility of a car having more than one VIN, then they identify different cars. 然而,如果它们具有不同的VIN,排除具有多于一个VIN的汽车的任何奇怪可能性,则它们识别不同的汽车。 Even if the cars have the same make and model, options packages, paint scheme, etc. they would still be different cars. 即使汽车有相同的品牌和型号,选项包,油漆方案等,他们仍然会是不同的汽车。 A person who bought one would not be entitled to arbitrarily start using the other instead. 购买一个人的人无权随意开始使用另一个人。 It may sometimes be useful to know whether two cars presently have the same options packages, etc. but if that's what one wants to know, that's what one should ask. 有时候知道两辆车目前是否有相同的选项包等可能是有用的,但如果这是人们想知道的,那就是人们应该问的问题。

Guess: the current behavior of Object.Equals is not what most people consider to be "equal". 猜猜: Object.Equals的当前行为并不是大多数人认为的“平等”。

The main (only?) reason of this method to exist is to allow searching for items in collections by pretending to be "==" implementation. 这种方法存在的主要(唯一?)原因是允许通过伪装成“==”实现来搜索集合中的项目。 So in most practical cases this implementation behaves unexpectedly (except for the case when you want to find if particular instance is in the collection already) and you force to provide you custom comparison functions... 因此,在大多数实际情况下,此实现会出现意外行为(除非您想要查找特定实例是否已在集合中),并强制为您提供自定义比较函数...

Likely it is method of Object because for technical reasons. 可能它是Object的方法,因为技术原因。 Ie for Array/Dictionary it may be faster to assume all objects have Equal / GetHash instead of checking something on object to enable "Find" functionality. 即对于Array / Dictionary,假设所有对象都具有Equal / GetHash而不是检查对象上的某些内容以启用“Find”功能可能会更快。

Arguably it should not be on Object at all and instead just require classes that can be stored in collections to implement some form of IComparable interface. 可以说它根本不应该在Object上,而只需要可以存储在集合中的类来实现某种形式的IComparable接口。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM