C#(.NET)的object类里面有三个关于判断相等性的方法:安全
还有一个接口:IEquatable<T>也能够用来判断相等性。性能
比较这个Class的两个实例,它们的属性值是同样的:3d
输出结果:blog
之因此结果是False,是由于object.Equals()评估的是引用的相等性,除非进行了重写。继承
这是两个字符串,并且使用string.Copy()能够保证它们不指向同一个地址(若是不使用string.Copy(),而直接赋两个一样的值,那么可能会发生字符串驻留问题:https://www.cnblogs.com/artech/archive/2007/03/04/663728.aspx):接口
这时输出的结果是:字符串
可是咱们看一下string这个类,能够发现string有不少Equals()方法:get
若是按照上面这么写的话,它并无调用object.Equals()方法。因此咱们改一下代码:源码
这时调用的是object.Equals()方法,它的输出依然是:string
这是由于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。
使用object virtual的Equals()方法能够应付大部分状况,可是若是该引用是null,那么使用该方法就会报错了:
这时候咱们就可使用object类的静态Equals()方法:
(也能够不写object)
而结果固然是:
结果是:
在.NET/.NET Core 里面,null和null是相等的。
静态Equals()方法的源码其实很简单,除了检查null以外,它会给出和virtual Equals()方法一样的结果。
若是你对virtual的Equals()方法进行了重写,而因为静态的Equals()方法就会调用重写的virtual Equals()方法,因此这两个方法要保持一向性。
它和前两种方法有点像,可是也不尽相同。
虽然virtual和静态的Equals()方法一般会比较引用,可是virutal的方法能够被重写,从而比较的是值,例如string。因此使用ReferenceEquals()来比较两个变量是否指向同一个实例是更安全准确的。
看下面这两个比较:
第一个比较调用的是object的virtual Equals()方法,可是string对其进行了重写,比较的是值:
而第二个比较是object的静态的ReferenceEquals()方法,因为是静态的,因此无法重写:
而C#里的==是什么原理,之后再说。
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()方法应该保持一致,作一样的事。