使Equals成为常用方法的目的是什么?

时间:2009-12-03 08:29:20

标签: c# language-design

这不是一个如何实现它的问题,但这个方法的目的是什么?我的意思是 - 好吧,我知道在搜索时需要这样做,但为什么它被埋没为“对象”类的方法?

故事是这样的 - 我有一些类,默认情况下(逻辑意义上)对象不具有可比性。每次要比较/搜索它们时,都必须准确指定匹配方式。在这种情况下最好的是:

  1. 没有像Equals这样无处不在的方法,问题解决了,没有程序员(我班级的用户)会因为在搜索时省略自定义匹配而陷入陷阱

    但由于我无法改变C#

  2. 隐藏继承的,不需要的方法来阻止调用(编译时)

    但这也需要更改为C#

  3. 覆盖Equals并抛出异常 - 至少在运行时通知程序员

  4. 所以我问,因为我被迫丑陋(c),因为(b)是不可能的,因为缺乏(a)。

    简而言之 - 迫使所有对象具有可比性(Equals)的原因是什么?对我来说,这是一个过分的假设。提前感谢您的启发: - )。

3 个答案:

答案 0 :(得分:7)

我同意它在.NET和Java中基本上都是错误的。 GetHashCode也是如此 - 每个对象都有一个监视器。

它确实在仿制药之前让更有意义 - 但是对于泛型来说,压倒Equals(object)总觉得非常可怕。

blogged about this前一段时间 - 您可能会发现帖子和评论都很有趣。

答案 1 :(得分:2)

您忘记了选项4:什么都不做,让默认引用相等。没什么大不了的IMO。即使使用自定义匹配选项,您也可以选择默认选项(我会选择最严格的选项)并使用它来实现Equals()。

答案 2 :(得分:0)

假设有人拥有动物名单,并且希望将两个项目相互比较:一个Cat实例和一个Dog实例。如果Cat实例被询问它是否与Dog实例相同,那么cat是否更有意义抛出InvalidTypeException,或者它只是说“不,它不相等”。

Equals方法应遵守两条规则:

  1. 相等的互易性:对于任何X和Y,当且仅当Y.Equals(X)为真时,X.Equals(Y)才为真。
  2. Liskov替换原则:如果Q类派生自P,则可以用P完成可以用Q完成的操作。

这些共同意味着如果Q派生自P,则类型P的对象必须可以在类型Q的对象上调用Equals,这反过来意味着类型Q的对象必须可以调用等于P类型的对象。此外,如果R也是从P派生的,则类型Q的对象必须可以在类型R的对象上调用Equals(无论R是否与Q相关)。

虽然所有对象实现Equals可能并不是绝对必要的,但是对于所有类来说,拥有单个Equals(Object)要比为不同的基类型使用各种Equals方法要清晰得多,所有这些都必须被覆盖具有相同的语义,以避免奇怪的行为。

修改/附录

Object.Equals的存在是为了回答这个问题:给定两个对象引用X和Y,对象X可以承诺不使用ReferenceEquals,Reflection等的外部代码将无法显示X和Y不引用到同一个对象实例?对于任何对象X,X.Equals(X)必须为true,因为外部代码不可能显示X与X的实例不同。此外,如果X.Equals(Y)“合法”为真,则Y.Equals(X) )也必须是真的;如果没有,X.Equals(X)(这是真的)与Y.Equals(X)不匹配的事实将是一个可证明的差异,暗示X.Equals(Y)应该是假的。

对于浅可变类型,如果X和Y引用不同的对象实例,通常可以通过改变X并观察Y(*)中是否发生相同的突变来证明这一点。如果可以使用这种变异来证明X和Y是不同的对象实例,那么X.Equals(Y)应该返回true。两个包含相同字符的String对象报告它们彼此相等的原因不仅仅是它们碰巧在比较时包含相同的字符,更重要的是如果一个的所有实例都被另一个替换,只有代码使用ReferenceEquals,Reflection或其他此类技巧的人甚至会注意到。

(*)有可能有一个浅的可变类的两个不同但不可区分的实例X和Y,它们都互相引用,这样那些会改变一个的方法也会改变另一个。如果外部代码没有办法区分实例,那么可以合法地将X.Equals(Y)报告为真(反之亦然)。另一方面,我无法想到这样一个类对于X和Y都持有对共享可变对象的不可变引用的类更有用。请注意,X.Equals(Y)不要求X和Y为深度不可变;它只要求应用于X的任何突变对Y都有相同的效果。