C#相等性 - 三个方法和一个接口
简介
C#(.NET)的object类里面有三个关于判断相等性的方法:
- public virtual bool Equals(object obj)
- public static bool Equals(object objA, object objB)
- public static bool ReferenceEquals(object objA, object objB)
还有一个接口:IEquatable<T>也可以用来判断相等性。
virtual bool Equals()
比较自定义Class

比较这个Class的两个实例,它们的属性值是一样的:

输出结果:

之所以结果是False,是因为object.Equals()评估的是引用的相等性,除非进行了重写。
比较string
这是两个字符串,而且使用string.Copy()可以保证它们不指向同一个地址(如果不使用string.Copy(),而直接赋两个同样的值,那么可能会发生字符串驻留问题:https://www.cnblogs.com/artech/archive/2007/03/04/663728.aspx):

这时输出的结果是:

但是我们看一下string这个类,可以发现string有很多Equals()方法:

如果按照上面这么写的话,它并没有调用object.Equals()方法。所以我们改一下代码:

这时调用的是object.Equals()方法,它的输出依然是:

这是因为string类对object的Equals()方法进行了重写,重写后比较的是字符串的值。
除了string之外,delegates和Tuples也对object.Equals()方法进行了重写。不过对大部分的.NET类型来说,object.Equals()比较的是引用。
比较值类型
值类型是存放在Stack上面的,它们通常没有引用,除非你对它们进行装箱操作。
那么对值类型使用object.Equals()方法,应该没有什么意义。。。
有这么一个自定义的Struct:

然后进行两组比较:

输出结果是:

很显然,结果有点出乎我的意料,针对这个Struct类型,object.Equals()比较的是它们的值。
这是因为所有的struct都继承于System.ValueType,而System.ValueType继承于System.Object,System.ValueType它对object.Equals()方法进行了重写,重写的方法里会比较值类型里面所有的字段(Field),如果所有字段都相等,那么就返回true。
但是System.ValueType的重写是使用反射来找到所有的字段(Fields),所以性能比较差。
所以针对值类型最好的办法是自己重写一下Equals()方法。
总结
默认情况下,针对引用类型,object.Equals()比较的是引用;针对值类型,object.Equals()比较的是值。
但是所有的类型都可以重写object.Equals()方法,例如string。
静态的 Equals() 方法
比较null
使用object virtual的Equals()方法可以应付大部分情况,但是如果该引用是null,那么使用该方法就会报错了:

这时候我们就可以使用object类的静态Equals()方法:

(也可以不写object)
而结果当然是:

比较两个null

结果是:

在.NET/.NET Core 里面,null和null是相等的。
源码

静态Equals()方法的源码其实很简单,除了检查null之外,它会给出和virtual Equals()方法同样的结果。
如果你对virtual的Equals()方法进行了重写,而由于静态的Equals()方法就会调用重写的virtual Equals()方法,所以这两个方法要保持一贯性。
静态 Reference Equals() 方法
它和前两种方法有点像,但是也不尽相同。
虽然virtual和静态的Equals()方法通常会比较引用,但是virutal的方法可以被重写,从而比较的是值,例如string。所以使用ReferenceEquals()来比较两个变量是否指向同一个实例是更安全准确的。
看下面这两个比较:

第一个比较调用的是object的virtual Equals()方法,但是string对其进行了重写,比较的是值:

而第二个比较是object的静态的ReferenceEquals()方法,由于是静态的,所以没法重写:

而C#里的==是什么原理,以后再说。
IEquatable<T>
System.Object的static bool Equals(object obj)这个方法,因为其参数是object类型,所以它可以对任何引用类型进行比较。但是如果想比较值类型的话,那么值类型就会被装箱,然后再进行比较。但是装箱的动作会有性能损耗,而之所以采用值类型的主要原因就是因为性能。所以这是一个问题。
再者,使用该方法来比较两个不相干的类型,比如Apple和Book这两个Class,比较的时候不会报错,但是这没有任何意义。这就是因为参数不是强类型,才会出现这些问题。
而IEquatable<T>这个接口就可以解决这些问题。
它只定义了一个方法:bool Equals(T other)。
例子,三个int:

使用它的Equals()方法:

可以看到除了object.Equals(object obj)这个方法外,它还有一个Equals(int obj)这个方法,它的参数是强类型的,这是因为int实现了IEquatable<T>接口。
而其源码大致如下:

所以平时比较int的时候使用==即可。
所有的原始类型都实现了IEquatable<T>接口。int, byte...
而IEquatable<T>对值类型非常有用。
但是对引用类型没有太大的用处,因为引用类型比较时不存在装箱问题,而且IEquatable<T>在继承方面还是存在问题的,但是string还是实现了IEquatable<T>接口,因为string是seal的,不存在继承。
需要注意的是如果实现了IEquatable<T>,那么它的实现方法和重写的object.Equals()方法应该保持一致,做同样的事。
C#相等性 - 三个方法和一个接口的更多相关文章
- 如何定义一个接口(接口Interface只在COM组件中定义了,MFC和C++都没有接口的概念)
接口是COM中的关键词,在c++中并没有这个概念.接口是一种极度的抽象.接口用在COM组件中有自己的GUID值,因此定义接口时一定要指定它的GUID值. 实际上接口就是struct,即#define ...
- List遍历三种方法:1.for 2.增强性for 3.迭代器
package chapter09; import java.util.ArrayList;import java.util.Iterator;import java.util.List; /* * ...
- python每次处理一个字符的三种方法
python每次处理一个字符的三种方法 a_string = "abccdea" print 'the first' for c in a_string: print ord(c) ...
- 三种方法教你HTML实现点击某一个元素之外触发事件
HTML实现点击某一个元素之外触发事件 大致编写的HTML界面渲染后是这个样子的,我们现在想要实现的需求是点击Button所在的div不会触发事件,而在点击Button所在的div之外的区域时会触发事 ...
- YbSoftwareFactory 代码生成插件【二十五】:Razor视图中以全局方式调用后台方法输出页面代码的三种方法
上一篇介绍了 MVC中实现动态自定义路由 的实现,本篇将介绍Razor视图中以全局方式调用后台方法输出页面代码的三种方法. 框架最新的升级实现了一个页面部件功能,其实就是通过后台方法查询数据库内容,把 ...
- 基于保守性和规则性的预测方法SIFT和PolyPhen
有什么特征可以帮助我们来区分导致功能和表型变化的变异和其他变异,然后我们如何综合特征来做出一个预测模型? 表型或功能的改变(phenotypical/functional effect)a,个体表型上 ...
- C#中??和?分别是什么意思? 在ASP.NET开发中一些单词的标准缩写 C#SESSION丢失问题的解决办法 在C#中INTERFACE与ABSTRACT CLASS的区别 SQL命令语句小技巧 JQUERY判断CHECKBOX是否选中三种方法 JS中!=、==、!==、===的用法和区别 在对象比较中,对象相等和对象一致分别指的是什么?
C#中??和?分别是什么意思? 在C#中??和?分别是什么意思? 1. 可空类型修饰符(?):引用类型可以使用空引用表示一个不存在的值,而值类型通常不能表示为空.例如:string str=null; ...
- html5--6-67 阶段练习8-弹性三列布局
html5--6-67 阶段练习8-弹性三列布局 学习要点 运用弹性盒子模型完成一个三列布局,加深对学过知识点的综合应用能力. 了解用百分比设置元素高度的方法. @charset="UTF- ...
- 干货分享:Essay写作收集论据的三个方法
在很多时候,中国留学生写出的Essay在西方学术界看来是存在plagiarism的情况.并不是说咱们写的所有东西都是抄袭,而是思维逻辑和利用证据的方式与西方权威的academic writing不同. ...
随机推荐
- 分享一下 常用的转换方法(例如:数字转金钱,文本与html互转等)
public sealed class SAFCFormater { /// <summary> /// 文本格式到HTML /// </summary> /// <pa ...
- 搜索应用参考示例XXL-SEARCH
<搜索应用参考示例XXL-SEARCH> 一.简介 1.1 概述 XXL-SEARCH 是以 "lucene/elasticsearch" 为核心的,Pragmatic ...
- 【已解决】C#中往SQLServer插入数据时遇到BUG
错误信息如下: “System.Data.SqlClient.SqlException”类型的未经处理的异常在 System.Data.dll 中发生 其他信息: “”附近有语法错误. 文字版代码如下 ...
- Django入门一之安装及项目创建
1. 习惯性的创建虚拟环境 # 由于我安装也安装了pyhton3所以在前面要加python2 -m F:\Python Script\MyVirtualenv>python2 -m virtua ...
- 如何用Redis做LRU-Cache
LRU(Least Recently Used)最近最少使用算法是众多置换算法中的一种. Redis中有一个maxmemory概念,主要是为了将使用的内存限定在一个固定的大小.Redis用到的LRU ...
- 附近的人,附近的卖家(geohash+前缀树)
http://www.cnblogs.com/LBSer/p/3310455.html http://blog.csdn.net/shixiaoguo90/article/details/253137 ...
- Java bean和json互转时,屏蔽某个属性
有的时候我们把java bean 转换成json的时候,希望屏蔽掉某个属性,这时可以在java bean的属性上加上@JsonIgnore注解,在com.fasterxml.jackson.annot ...
- 2017年的golang、python、php、c++、c、java、Nodejs性能对比[续]
2017年的golang.python.php.c++.c.java.Nodejs性能对比[续] 最近忙,这个话题放了几天,今天来个续集. 上篇传送门: 2017年的golang.python.p ...
- Tair总述
Tair 是淘宝自己开发的一个分布式 key/value 存储引擎.tair 分为持久化和非持久化两种使用方式.非持久化的 tair 可以看成是一个分布式缓存.持久化的 tair 将数据存放于磁盘中. ...
- 安装Twisted
Python看到网络编程,讲到Twisted这个强大的网络框架,很有兴趣,配合 官方文档,打算研究一哈,但是一开始就碰壁了. 安装的时候 pip install Twisted报错了: 提示没有装什么 ...