8.2.3 覆写 Equals
经过对四种不同类型判等方法的讨论,我们不难发现不管是 Equals 静态方法、Equals 虚方法 抑或==操作符的执行结果,都可能受到覆写 Equals 方法的影响。因此研究对象判等就必须将注意 力集中在自定义类型中如何实现 Equals 方法,以及实现怎样的 Equals 方法。因为,不同的类型, 对于“相等”的理解会有所偏差,你甚至可以在自定义类型中实现一个总是相等的类型,例如:
class AlwaysEquals
{
public override bool Equals(object obj)
{
return true;
}
}
因此,Euqls 方法的执行结果取决于自定义类型的具体实现规则,而.NET 又为什么提供这种机 制来实现对象判等策略呢?首先,对象判等决定于需求,没有必要为所有.NET 类型完成逻辑判等, System.Object 基类也无法提供满足各种需求的判等方法;其次,对象判等包括值判等和引用判等 两个方面,不同的类型对判等的处理又有所不同,通过多态机制在派生类中处理各自的判等实现 显然是更加明智与可取的选择。
接下来,我们开始研究如何通过覆写 Equals 方法实现对象的判等。覆写 Equals 往往并非易事, 要综合考虑到对值类型字段和引用类型字段的分别判等处理,同时还要兼顾父类覆写所带来的影 响。不适当的覆写会引发意想不到的问题,所以必须遵循三个等价原则:自反、传递和对称,这 是实现 Equals 的通用契约。那么又如何为自定义类型实现 Equals 方法呢?
最好的参考资源当然来自于.NET 框架类库的实现,事实上,关于 Equals 的覆写在.NET 中已经 有很多的基本类型完成了这一实现。从值类型和引用类型两个角度来看:
对于值类型,基类 System.ValueType 通过反射机制覆写了 Equals 方法来比较两个对象的值相 等,但是这种方式并不高效,更明智的办法是在自定义值类型时有针对性的覆写 Equals 方法, 来提供更灵活、高效的处理机制。
对于引用类型,覆写 Equals 方法意味着要改变 System.Object 类型提供的引用相等语义。那么, 覆写 Equals 要根据类型本身的特点来实现,在.NET 框架类库中就有很多典型的引用类型实现 了值相等语义。例如 System.String 类型的两个变量相等意味着其包含了相等的内容,System. Version 类型的两个变量相等也意味着其 Version 信息的各个指标分别相等。
因此对 Equals 方法的覆写主要包括对值类型的覆写和对引用类型的覆写,同时也要区别基类 是否已经有过覆写和不曾覆写两种情况,并以等价原则为前提,进行判断。在此,我们仅提供较 为标准的实现方法,具体的实现取决于不同的类型定义和语义需求。
class EqualsEx
{
//定义值类型成员 ms
private MyStruct ms;
//定义引用类型成员 mc
private MyClass mc;
public override bool Equals(object obj)
{
//为 null,则必不相等
if (obj == null) return false;
//引用判等为真,则二者必定相等
if (ReferenceEquals(this, obj)) return true;
//类型判断
EqualsEx objEx = obj as EqualsEx;
if (objEx == null) return false;
//最后是成员判断,分值类型成员和引用类型成员
//通常可以提供强类型的判等方法来单独处理对各个成员的判等
return EqualsHelper(this, objEx);
}
private static bool EqualsHelper(EqualsEx objA, EqualsEx objB)
{
//值类型成员判断
if (!objA.ms.Equals(objA.ms)) return false;
//引用类型成员判断
if (!Equals(objA.mc, objB.mc)) return false;
//最后,才可以判定两个对象是相等的
return true;
}
} internal struct MyStruct
{
} internal class MyClass
{
}
上述示例只是从标准化的角度来阐释 Equals 覆写的简单实现,而实际应用时又会有所不同, 然而总结起来实现 Equals 方法我们应该着力于以下几点:首先,检测 obj 是否为 null,如果是则 必然不相等;然后,以 ReferenceEquals 来判等是否引用相等,这种办法比较高效,因为引用相等 即可以推出值相等;然后,再进行类型判断,不同类型的对象一定不相等;最后,也是最复杂的 一个过程,即对对象的各个成员进行比较,引用类型进行恒定性判断,值类型进行恒等性判断。 在本例中我们将成员判断封装为一个专门的处理方法 EqualsHelper,以隔离对类成员的判断实现, 主要有以下几个好处:
符合 Extract Method 原则,以隔离相对变化的操作。
提供了强类型版本的 Equals 实现,对于值类型成员来说还可以避免不必要的装箱操作。
为==操作符提供了重载实现的安全版本。
在.NET 框架中,System.String 类型的 Equals 覆写方法就提供了 EqualsHelper 方法来实现。
8.2.3 覆写 Equals的更多相关文章
- [改善Java代码]覆写equals方法必须覆写hashCode方法
覆写equals方法必须覆写hashCode方法,这条规则基本上每个Javaer都知道,这也是JDK API上反复说明的,不过为什么要这样做呢?这两个方法之间有什么关系呢?本建议就来解释该问题,我们先 ...
- [改善Java代码]覆写equals方法时不要识别不出自己
建议45: 覆写equals方法时不要识别不出自己 我们在写一个JavaBean时,经常会覆写equals方法,其目的是根据业务规则判断两个对象是否相等,比如我们写一个Person类,然后根据姓名判断 ...
- java覆写equals方法
何时需要重写equals() 当一个类有自己特有的“逻辑相等”概念(不同于对象身份的概念). object规范规定,如果要重写equals(),也要重写hashcode() 如何覆写equals() ...
- 为什么覆写equals()方法的时候总是要覆写hashcode()?
要回答这个问题,我们应该先认识一下obj中的equals和hascode方法 1.equals()方法在obj中定义如下: public boolean equals(Object obj) { re ...
- 为什么覆写equals必须要覆写hashCode?
============================================= 原文链接: 为什么覆写equals必须要覆写hashCode? 转载请注明出处! ============= ...
- 覆写equals方法为什么需要覆写hashCode方法
覆写equals方法必须覆写hashCode方法,是JDK API上反复说明的,不过为什么要这样做呢?这两个方法之间有什么关系呢? void test() { // Person类的实例作为Map的k ...
- (强制)要求覆写equals必须覆写hashCode(原理分析)
hashCode和equals hashCode和equals用来标识对象,两个方法协同工作可用来判断两个对象是否相等.众所周知,根据生成的哈希将数据散列开来,可以使存取元素更快.对象通过调用Obje ...
- 11.JAVA-Object类之finalize(),clone(),toString()等方法覆写
1.Object介绍 Object类是一个特殊的类,是所有类(包括数组,接口 )的父类,如果一个类没有用extends明确指出继承于某个类,那么它默认继承Object类,所以可以通过向上转型的方法使用 ...
- object 类 toString() 和 equals() 的覆写
基本作用: objiect类是所有类的父类. 任何一个类定义的时候如果没有明确定义了父类的话,默认父类是Object类. class A extends Object{} 在整个java里面,类的继承 ...
随机推荐
- 项目构建之maven篇:1.环境搭建
maven下载: 下载地址 环境变量设置 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd29iZW5kaWFua3Vu/font/5a6L5L2T/fon ...
- 5分钟Serverless实践 | 构建无服务器的敏感词过滤后端系统
前言 在上一篇“5分钟Serverless实践”系列文章中,我们介绍了什么是Serverless,以及如何构建一个无服务器的图片鉴黄Web应用,本文将延续这个话题,以敏感词过滤为例,介绍如何构建一个无 ...
- 数据库定时删除历史数据的SQL指令
新建作业,用于定时清除数据库的历史数据 删除表gps_comm 之前120天即一个季度的数据,例如以下: ELETE FROM gps_comm WHERE (时间 < GETDATE() - ...
- ALSA声卡驱动中的DAPM详解之三:如何定义各种widget
上一节中,介绍了DAPM框架中几个重要的数据结构:snd_soc_dapm_widget,snd_soc_dapm_path,snd_soc_dapm_route.其中snd_soc_dapm_pat ...
- 第二章 在Html中使用JavaScript
https://www.jianshu.com/p/8247a9401725 2.1 Script元素 https://developer.mozilla.org/en-US/docs/Web/HTM ...
- 编码格式(UTF-8 与 ANSI)各种编码解码(encode、decode)
Windows:默认为 ANSI,记事本程序另存为处,可以设置其他编码格式: Ubuntu:默认为 UTF-8 1. ANSI ANSI 编码表示英文字符时用一个字节,表示中文用两个或四个字节 -- ...
- P2120 [ZJOI2007]仓库建设 斜率优化dp
好题,这题是我理解的第一道斜率优化dp,自然要写一发题解.首先我们要写出普通的表达式,然后先用前缀和优化.然后呢?我们观察发现,x[i]是递增,而我们发现的斜率也是需要是递增的,然后就维护一个单调递增 ...
- bzoj3011
可并堆 复习一下可并堆 维护一个大跟堆,每次把节点儿子打上边权标记,然后合并,可并堆上维护一个size,每次把大于l的弹出,size就是答案 程序中那个d写l和r速度差不多,我写l是表示右儿子到u的最 ...
- [App Store Connect帮助]二、 添加、编辑和删除用户(6)生成 API 密钥
如果已批准您访问 App Store Connect API,您可以生成 API 密钥,以便使用该密钥配置.认证和使用 App Store Connect 服务. 有关管理和保护您密钥的更多信息,请参 ...
- selenium3 + python - 异常处理截图 screenshot
一.截图方法 1.get_screenshot_as_file(self, filename) --这个方法是获取当前window的截图,出现IOError时候返回False,截图成功返回True. ...