为了账号安全,请及时绑定邮箱和手机立即绑定

什么是比较参考类型的两个实例的“最佳实践”?

什么是比较参考类型的两个实例的“最佳实践”?

慕尼黑的夜晚无繁华 2019-08-27 13:06:07
我最近遇到过这种情况,到目前为止,我一直在愉快地重写等于运算符(==)和/或Equals方法,以查看两个引用类型是否实际包含相同的数据(即两个看起来相同的不同实例)。我一直在使用它,因为我已经进行了更多的自动化测试(比较参考/预期数据与返回的数据)。在查看MSDN中的一些编码标准指南时,我遇到了一篇建议反对它的文章。现在我理解为什么文章说这个(因为它们不是同一个实例)但它没有回答这个问题:比较两种参考类型的最佳方法是什么?我们应该实施IComparable吗?(我还看到提到这应该仅为值类型保留)。有一些我不知道的界面吗?我们应该自己动手吗?!非常感谢^ _ ^更新看起来我错误地阅读了一些文档(这是漫长的一天)并且压倒Equals可能是要走的路。如果要实现引用类型,则应考虑在引用类型上覆盖Equals方法(如果类型看起来像基本类型,如Point,String,BigNumber等)。大多数引用类型不应重载等于运算符,即使它们重写等于。但是,如果要实现旨在具有值语义的引用类型(例如复数类型),则应覆盖相等运算符。
查看完整描述

3 回答

?
手掌心

TA贡献1942条经验 获得超3个赞

下面我总结了实现IEquatable时需要做的事情,并提供了各种MSDN文档页面的理由。


摘要

  • 当需要测试值相等时(例如在集合中使用对象时),您应该为您的类实现IEquatable接口,覆盖Object.Equals和GetHashCode。

  • 当需要测试参考相等性时,您应该使用operator ==,operator!=和Object.ReferenceEquals。

  • 您应该只覆盖operator ==和operator!=的值类型和不可变的引用类型。


理由

IEquatable

System.IEquatable接口用于比较对象的两个实例是否相等。根据类中实现的逻辑比较对象。比较结果是一个布尔值,表示对象是否不同。这与System.IComparable接口形成对比,后者返回一个整数,指示对象值的不同之处。

IEquatable接口声明了必须重写的两个方法。Equals方法包含执行实际比较的实现,如果对象值相等则返回true,否则返回false。GetHashCode方法应返回唯一的哈希值,该哈希值可用于唯一标识包含不同值的相同对象。使用的散列算法类型是特定于实现的。

IEquatable.Equals方法

  • 您应该为对象实现IEquatable,以处理它们存储在数组或泛型集合中的可能性。

  • 如果实现IEquatable,还应该覆盖Object.Equals(Object)和GetHashCode的基类实现,以便它们的行为与IEquatable.Equals方法的行为一致。

覆盖等于()和运算符的指南==(C#编程指南)

  • x.Equals(x)返回true。

  • x.Equals(y)返回与y.Equals(x)相同的值

  • if(x.Equals(y)&& y.Equals(z))返回true,则x.Equals(z)返回true。

  • 连续调用x。只要未修改x和y引用的对象,Equals(y)就会返回相同的值。

  • X。Equals(null)返回false(仅适用于非可空值类型。有关更多信息,请参阅Nullable Types(C#编程指南)。)

  • Equals的新实现不应该抛出异常。

  • 建议任何覆盖Equals的类也会覆盖Object.GetHashCode。

  • 建议除了实现Equals(object)之外,任何类还为自己的类型实现Equals(type),以增强性能。

默认情况下,operator ==通过确定两个引用是否指示同一对象来测试引用相等性。因此,引用类型不必实现operator ==以获得此功能。当一个类型是不可变的,也就是说,实例中包含的数据不能改变时,重载operator ==来比较值的相等而不是引用相等可能是有用的,因为作为不可变对象,它们可以被认为是相同的因为它们具有相同的价值。在非不可变类型中覆盖operator ==不是一个好主意。

  • 重载的operator ==实现不应该抛出异常。

  • 任何重载operator ==的类型也应该重载operator!=。

==运算符(C#参考)

  • 对于预定义的值类型,如果操作数的值相等,则相等运算符(==)返回true,否则返回false。

  • 对于除string之外的引用类型,如果其两个操作数引用同一对象,则==返回true。

  • 对于字符串类型,==比较字符串的值。

  • 在运算符==覆盖中使用==比较测试null时,请确保使用基础对象类运算符。如果不这样做,将发生无限递归,从而导致堆栈溢出。

Object.Equals方法(对象)

如果您的编程语言支持运算符重载,并且您选择重载给定类型的相等运算符,则该类型必须覆盖Equals方法。Equals方法的此类实现必须返回与相等运算符相同的结果

以下准则用于实现值类型

  • 考虑重写Equals以获得比ValueType上的Equals的默认实现所提供的性能更高的性能。

  • 如果重写等于并且语言支持运算符重载,则必须为值类型重载等于运算符。

以下准则用于实现引用类型

  • 如果类型的语义基于类型表示某些值的事实,请考虑在引用类型上覆盖Equals。

  • 大多数引用类型都不能重载等于运算符,即使它们重写等于。但是,如果要实现旨在具有值语义的引用类型(例如复数类型),则必须覆盖相等运算符。


额外的陷阱

  • 重写GetHashCode()时,请确保在哈希代码中使用它们之前测试NULL的引用类型。

  • 我遇到了这里描述的基于接口的编程和运算符重载的问题:C#中基于接口的编程的运算符重载


查看完整回答
反对 回复 2019-08-27
  • 3 回答
  • 0 关注
  • 319 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信