1、引言

  最近在看《CLR via C#》看到对象判等的那一节,觉得这也是.NET基础知识中比较重要的部分就写一篇博文来简单的总结归纳一下。

2、.NET下的对象判等

  在.NET中关于对象判等有四个方法。依次是:System.Object.ReferenceEquals、System.Object.Equals、Instance.Equals(实例方法)、Operator==操作符。下面我们就来比较下这四个方法的异同点。

  我们打开ILSpy反编译软件,看一下System.Object基类中关于对象判等的方法。如图所示:

  2.1 System.ReferenceEquals方法

  我们看到System.Object.ReferenceEquals方法比较的是两个引用对象的内存地址。即:如果两个引用类型的指针都指向堆上面的同一个对象,那么返回True,否则返回False。注意:对于两个值类型调用该方法会永远返回False,因为值类型在转化为Object类型的时候需要进行装箱(在堆上分配对象),两个值类型的对象会在堆上面分配两个对象。所以永远返回False

  2.2 Operator ==操作符

  ==操作符在.NET中根据值类型和引用类型的的不同会有不同的默认行为。当比较的双方是引用类型时,==操作符默认比较的是两个对象的内存地址。即与System.Object.Refernece方法一致。当比较的双方是值类型(编译器内置的基元类型时)是比较值是否相等。

  2.3 System.Object.Equals方法

  通过上面的源码我们看到这个方法主要依赖于==操作符和Equals的实例方法。如果objA,objB两个对象指向同一个内存对象那么就返回True。否则的话比较结果就会依赖于Instance.Equals方法。

  2.4 Instance.Equals方法

  我们通过上面的源码分析,可以看到return RuntimeHelpers.Equals(this, obj);这一句代码。其实在内部System.Object提供的这个Equals实例方法的逻辑很简单。就是比较obj==this。显然这个对于我们自定义的类型比较是不合理的。这个方法是virtual的,我们可以重写这个方法来自定义我们的比较规则。下面来看一下具体的重写规则。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks; namespace CryptUntility
{
class BaseClass
{
public int baseFieldInt;
public string baseFiledString;
private bool flag; public BaseClass(int filedInt, string fieldString,bool flag)
{
this.baseFieldInt = filedInt;
this.baseFiledString = fieldString;
this.flag = flag;
} public override bool Equals(object obj)
{
//如果obj为null肯定不相等,因为调用该方法的this不可能是null,否则会出现异常
if (obj == null)
{
return false;
}
//如果引用的是同一个对象,肯定相等
if (ReferenceEquals(this, obj))
{
return true;
}
//是否能够转型到DevideClass,如果不能说明不是同一类型的,肯定不相等
BaseClass baseClass = obj as BaseClass;
if (baseClass == null)
{
return false;
}
//接下来依次比较各个值是否相等
if (FiledEqule(this, baseClass))
{
return true;
}
return false;
} private bool FiledEqule(BaseClass obj1, BaseClass obj2)
{
if (obj1.baseFieldInt == obj2.baseFieldInt
&& obj1.baseFiledString.Equals(obj2.baseFiledString)
&& obj1.flag == obj2.flag)
{
return true;
}
return false;
}
} class DevideClass : BaseClass
{
public int devideFidldInt;
public string devideFieldString; public DevideClass(int filedInt, string fieldString, bool flag)
: base(filedInt, fieldString, flag)
{
this.devideFidldInt = filedInt;
this.devideFieldString = fieldString;
} public override string ToString()
{
return String.Format("{0},{1}", devideFidldInt.ToString(), devideFieldString);
} /// <summary>
/// 重写比较规则
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public override bool Equals(object obj)
{
//如果obj为null肯定不相等,因为调用该方法的this不可能是null,否则会出现异常
if (obj == null)
{
return false;
}
//如果引用的是同一个对象,肯定相等
if(ReferenceEquals(this,obj))
{
return true;
}
//是否能够转型到DevideClass,如果不能说明不是同一类型的,肯定不相等
DevideClass devideObj = obj as DevideClass;
if (devideObj == null)
{
return false;
}
//接下来依次比较各个值是否相等
if (FiledEqule(this, devideObj))
{
/**
* 如果当前类是从System.Object类继承的那么不需要调用base方法
* 否则需要调用base(基类方法)来比较基类型的字段
* 因为有些基类字段有可能是私有的,需要将其提交到基类进行比较
* */
return base.Equals(devideObj);
}
return false;
} private bool FiledEqule(DevideClass obj1, DevideClass obj2)
{
if (obj1.devideFidldInt == obj2.devideFidldInt
&& obj1.devideFieldString.Equals(obj2.devideFieldString)
&& obj1.baseFieldInt == obj2.baseFieldInt
&& obj1.baseFiledString.Equals(obj2.baseFiledString))
{ return true;
}
return false;
}
} class Program
{
static void Main(string[] args)
{
DevideClass dev1 = new DevideClass(120, "GG", true);
DevideClass dev2 = new DevideClass(120, "GG", false); Console.WriteLine("dev1==dev2:"+dev1.Equals(dev2)); Console.Read();
}
}
}

  在以上的代码中我们看到我们在重写的时候具体的流程如下:

  1、第一步判断参数obj是否为null。作为实例方法那么this对象本身肯定不能是null,否则的话爆出异常的。如果obj==null那么肯定不相等。

  2、使用ReferenceEquals方法来判断两个对象是否指向同一个内存对象。这是加快对象判断的一个方法。

  3、接下来我们可以尝试着对需要比较的对象进行转型,如果不能转型成功,那么与this对象不是同一类型的对象。类型不同肯定不会相等。

  4、接下来我们可以依次比较我们自定义对象的各个字段,使用系统内置的Equals来进行比较。注意:如果一个类型的上一级基类不是System.Object类型,那么在进行字段比较的时候需要调用基类的方法进行比较。因为基类中可能有些private字段是需要通过基类的Equals方法来来进行验证的。

3、最后的注意点

  我们重新Equals方法的同时也必须重新GetHashCode方法。相关内容我有机会会写出了和大家分享。大家也可以网上查阅相关的知识点。

.NET对象判等归纳与总结的更多相关文章

  1. [No0000B5]C# 类型基础 值类型和引用类型 及其 对象判等 深入研究1

    引言 本文之初的目的是讲述设计模式中的 Prototype(原型)模式,但是如果想较清楚地弄明白这个模式,需要了解对象克隆(Object Clone),Clone其实也就是对象复制.复制又分为了浅度复 ...

  2. JaveScript对象(JS知识点归纳七)

    1.JS中的对象表示的是一个具体的事物. a)静态的特征=>对象的属性 b)动态的行为=>对象的方法=>保存的值==>函数 2.对象的创建方式 a)构造函数的创建方式 ``` ...

  3. JaveScript内置对象(JS知识点归纳八)

    1)JS自身提供的方式 用于对数据进行简便的操作,根据方法可以操作的数据类型不同,形成了不同的对象--内置对象 2)数组 ​ a)基本操作方法--对数组进行修改 从数组最后进行操作 1)数组.push ...

  4. iOS判断对象相等 重写isEqual、isEqualToClass、hash

    相等的概念是探究哲学和数学的核心,并且对道德.公正和公共政策的问题有着深远的影响. 从一个经验主义者的角度来看,两个物体不能依据一些观测标准中分辨出来,它们就是相等的.在人文方面,平等主义者认为相等意 ...

  5. Java(类与对象)

    1>对象判等 请输入并运行以下代码,得到什么结果? public class Test { public static void main(String[] args) { // TODO Au ...

  6. js判重

    1.两个数组,取出不重复的部分 var arr=[1,2,3]; var arr1=[1,2]; vat tmp=[]; for(let i in arr1){ if(!(arr.includes(a ...

  7. [No0000B9]C# 类型基础 值类型和引用类型 及其 对象复制 浅度复制vs深度复制 深入研究2

    接上[No0000B5]C# 类型基础 值类型和引用类型 及其 对象判等 深入研究1 对象复制 有的时候,创建一个对象可能会非常耗时,比如对象需要从远程数据库中获取数据来填充,又或者创建对象需要读取硬 ...

  8. Java判断对象是否为NULL

    Java使用反射判断对象是否为NULL 判断Java对象是否为null可以有两层含义: 第一层:  直接使用 object == null 去判断,对象为null的时候返回true,不为null的时候 ...

  9. Java中对象池的本质是什么?(实战分析版)

    简介 对象池顾名思义就是存放对象的池,与我们常听到的线程池.数据库连接池.http连接池等一样,都是典型的池化设计思想. 对象池的优点就是可以集中管理池中对象,减少频繁创建和销毁长期使用的对象,从而提 ...

随机推荐

  1. git使用相关文章的链接

    http://search.fishc.com/cse/search?s=14988791857133860392&q=git&partner=discuz http://bbs.fi ...

  2. shell替换一个或多个空格为逗号

    (1)把文本中的一个或者多个空格替换为逗号 文本内容如下: # cat text 1 2 4 2 2 3 4 3 3 4 4 4 4 2 4 (2)shell程序代码为: # cat text | s ...

  3. Java反序列化测试

    前言:有没有想过,如何将对象进行“加密”后写入磁盘?序列化帮你实现! 1.概念 序列化 (Serialization)将对象的状态信息转换为可以存储或传输的形式的过程.在序列化期间,对象将其当前状态写 ...

  4. tyvj2018 小猫爬山

    之前做过一道题"破锣摇滚乐队",把猫都编了号,每辆车只能装一些编号递增的猫,而且前一辆车的猫编号都比后一辆车小.那道题的DP状态是:f[i][j]表示装了前i只猫,使用了j辆车时第 ...

  5. CSS 命名管理 之 BEM

    好吧,将 BEM 简单的解释为 “Block-Element-Modifier“, 其实是个不负责任的做法.鬼知道 Block 是什么啊?所以,看了一些似懂非懂的中文解释之后,自己还是得去找些英文来读 ...

  6. Unity连Photon服务器入门详解

    Photon是目前比较好用的游戏服务器.目前网上对于Photon的服务器讲解比较少,最近也对Photon做了初步的了解,做一个极其详细的入门. 首先就是得下载Photon咯 https://www.p ...

  7. 当spring 容器初始化完成后执行某个方法

    在做web项目开发中,尤其是企业级应用开发的时候,往往会在工程启动的时候做许多的前置检查. 比如检查是否使用了我们组禁止使用的Mysql的group_concat函数,如果使用了项目就不能启动,并指出 ...

  8. 加州大学伯克利分校Stat2.2x Probability 概率初步学习笔记: Section 4 The Central Limit Theorem

    Stat2.2x Probability(概率)课程由加州大学伯克利分校(University of California, Berkeley)于2014年在edX平台讲授. PDF笔记下载(Acad ...

  9. 在CentOS上安装Sublime Text

    CentOS 是基于 Red Hat (RHEL) 的, 其中并没有包管理工具 apt. 最近需要在装了 CentOS 系统的服务器上安装Sublime Text, 到官网上看了一下, 对其他 (De ...

  10. BZOJ1180: [CROATIAN2009]OTOCI

    传送门 一遍AC,开心! $Link-Cut-Tree$最后一题 //BZOJ 1180 //by Cydiater //2016.9.18 #include <iostream> #in ...