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. 怎么给我的Office文档加密

    很多的用户朋友都可以熟练的使用office中的Word.Excel和PowerPoint文档,但大家对Office文档加密方式了解的并不多.Advanced Office Password Recov ...

  2. 【HDU 5698】瞬间移动(组合数,逆元)

    x和y分开考虑,在(1,1)到(n,m)之间可以选择走i步.就需要选i步对应的行C(n-2,i)及i步对应的列C(m-2,i).相乘起来. 假设$m\leq n$$$\sum_{i=1}^{m-2} ...

  3. 【OpenJ_POJ C16D】Extracurricular Sports(构造,找规律)

    题目求n个互不相同的数,满足其和为其lcm.我们把lcm看成一个线段,分割成长度不同的n份.当然分法有很多,我们只需要构造一个好想好写的.先分成两个二分之一,取其中一个二分之一再分成1/3和2/3,接 ...

  4. 【转】Handler学习笔记(一)

    一.Handler的定义: Handler主要接收子线程发送的数据, 并用此数据配合主线程更新UI,用来跟UI主线程交互用.比如可以用handler发送一个message,然后在handler的线程中 ...

  5. iOS异步下载下载进度条显示

    说到http异步下载,首先要知道其中的关键类. 关键类是NSURLConnection  NSURLRequest NSMutableURLRequest  委托是 NSURLConnectionDo ...

  6. 使用oracle存储过程遇到的坑

    之前一直都是用sqlserver 突然用oracle  蛋疼的连存储过程执行一个查询都不会 各种百度锕  现在记录下面的语法问题 orcale创建一个存储过程的语法. create or replac ...

  7. centos安装CODEBLOCKS

    装了好多次系统,每次装的时候都有要在网上各种查,太麻烦了.所以决定记录一下,以后用到的时候会方便一些.当然,本文来源于网络,取百家之长,最重要的是本人已验证过,说明对本系统是可行的. 在CentOS7 ...

  8. [USACO 2010 OPEN]SLIED

    传送门 这道题的题意描述简直有毒.题没看完一眼分层图,然后火速敲了个堆优化的dijkstra,然后就被样例教做人了QAQ 这里说的最坏的情况让我很迷茫?感觉很难判定到底什么是最坏的情况以及确定了最坏的 ...

  9. NOIp 0910 爆零记

    这套题是神犇chty出的. 刚拿到题的时候有点懵逼,因为按照一般的套路第一题都是一眼题,但是看到第一题后想了很多个算法和数据结构好像都不能很好的解决.然后就随手敲了个暴力去看T2. 嗯...文件名是b ...

  10. 非阻塞socket学习,select基本用法

    server #include <stdio.h> #include <winsock2.h> #include <iostream> #pragma commen ...