缘由主要有如下几点:安全
因此建议是:全部供外部使用的struct都实现相等性。ide
具体来讲:函数
重写object.Equals()方法,是避免了反射,由于System.ValueType里面对object.Equals()方法的重写实现以下:性能
这里用到了反射。spa
而实现IEquatable<T>.Equals()接口方法,能够避免装箱,而且保证类型安全。对象
而实现==和!=,也就容许值类型使用该操做符了,写起来更方便直观,易于理解。并且这两个操做符必须一同实现。blog
而重写object.GetHashCode(),则是一个最佳实践。接口
全部为值类型重定义相等性,一共分4步,每步都是必须的。string
先看实例struct:hash
有构造函数,涉及到一个enum,并重写了ToString()方法。
首先来实现IEquatable<T>接口。
(若是你使用resharper或者Rider,那么实现该接口的时候它会自动把object的Equals和GetHashCode方法都重写了,而且自动完成了有意义的代码)
这里面我对三个属性进行了比较,使用了==操做符。其中==对于string来讲就是比较值,而enum其实就是int,DateTime也是值类型,而且已经实现了相等性判断的功能。
这个代码是resharper生成的。
代码很简单,首先检查是否为null,而后检查这个object是否是一个Person,这里使用了 is 操做符,并把它转型为Person,赋给了一个叫作other的变量。最后调用的这个Equals()方法,是咱们上面写的那个强类型的方法,由于other变量的类型是Person。
可是这个方法仍然涉及到装箱操做,因此仍是IEquatable<T>的实现方法更快一些。
若是只重写了object.Equals()方法,而没有重写GetHasCode()方法,那么resharper会有提示:
这个很简单,直接调用强类型的Equals()方法便可,并且因为Person是值类型,因此不用检查null,值类型不会为null。
若是只实现了其中一个操做符,那么会报错的。
GetHashCode()这个方法会返回一个32位的哈希码,它表明着对象内容的哈希值。
而类型里拥有GetHashCode()方法(返回Hash)的真正目的是,容许该类型在内部使用HashTable的集合中能够做为Key,由于HashTable须要这些哈希码。例如Dictionary<TK, TV>。
为了让HashTable能够正确的工做,Hash码有一个要求:若是两个实例被认为是相等的,那么它们必须返回相同的hash码。若是没有实现这个要求,那么你可能会发现这个类型做为Dictionary的Key的时候,会有一些意想不到的结果。
因此若是重写了object.Equals()方法,那么就得重写object.GetHashCode()方法。
看一下resharper自动实现的代码:
这里使用了unchecked,防止抛出溢出异常。
Name是引用类型,可能为null,因此判断一下。
而后其它两个int和DateTime类型,微软都作好了其GetHashCode()的实现。
这里对它们进行异或操做。之因此使用397这个数,可能由于397是一个足够大的质数,能够致使溢出,并混淆各位,之因此使用质数,是由于用质数相乘会获得比用其余任意数相乘更均匀的结果。
结果如预期,OK。
在这几个动做里,实际的逻辑写在了IEquatable<T>.Equals()方法里,object.Equals()就是检查类型而后调用IEquatable<T>.Equals(),== 和 != 操做符也是调用IEquatable<T>.Equals(),而GetHashCode()则使用了按位异或。
最后再重复一次,为值类型定义相等性必定要实现上述4各步骤的5个方法。