首先先谈一下Equals()这个方法:测试
Equals()方法,来自于Object,是咱们常常须要重写的方法。此方法的默认实现大概是这样的:spa
1 public virtual bool Equals(object obj) 2 3 { 4 5 if(obj==null) return false; 6 7 if(GetType() != obj.GetType()) return false; 8 9 Return true; 10 11 }
由此能够看出,默认的实现其实比较的是两个对象的内存地址(==操做符默认比较内存地址)。值类型和string类型除外,由于全部值类型继承于System.ValueType()(System.ValueType()一样继承于Object,可是System.ValueType()自己倒是引用类型),而System.ValueType()对Equals()和==操做符进行了重写,是逐字节比较的。而string类型是比较特殊的引用类型,因此strIng在不少地方都是特殊处理的,此处就不作深究了。3d
说完Equals()后再来聊一聊GetHashCode()。code
其实GetHashCode()在操做值类型的时候也是被System.ValueType()重写的。通过楼主测试的几个经常使用值类型来看,值类型的GetHashCode()基本都是原值输出(特指整数,Int32除外),真实性有待验证。结果以下:对象
说完值类型,说一下引用类型,先看下面这张运行结果:blog
从上图的结果能够看出,虽然string是引用类型,可是只要值同样,返回的HashCode也是同样的,这取决于它的特殊性。而咱们本身写的类型Coordinates一样的值但返回的HashCode却不同,咱们能够简单的理解为是coor1与coor2的内存地址不一样,因此CLR认为它们是不同的。继承
Ps:在程序的生命周期中,相同的对象、变量返回的HashCode是相同的,而且是惟一的。可是绝对不容许作持久性存储,程序一旦结束并从新启动后,一样的对象没法得到上次程序运行时的HashCode。生命周期
了解了两个方法后,开始今天的重点话题。内存
其实在上面的两个对象中(coor1、coor2),coor1.Equals(coor2)的返回结果为false(由于内存地址不一样),若是咱们想让它们的返回结果为true的话,只能重写Equals方法(以下图)。string
重点来了,重写完Equals之后,vs发出了警告,虽然程序猿历来都是无视警告的,但这个警告确实有必要了解一下,先来看下面这三段代码。
看完这三段代码,应该就理解为何要重写Equal时有必要重写GetHashCode了。
固然,若是你没打算在代码中使用Dictionary或HashTable就无所谓写不写了,换句话说,若是要把引用类型作为Dictionary或HashTable的key使用时,必须重写这两个方法。
缘由:当咱们把引用类型(string除外)作为Dictionary或HashTable的key时,有可能永远没法根据Key得到value的值,或者说两个类型的HashCode永远不会相等。就拿Dictionary来讲,虽然咱们存储的时候是键值对,可是CLR会先把key转成HashCode而且验证Equals后再作存储,根据key取值的时候也是把key转换成HashCode而且验证Equals后再取值,必定要注意验证时HashCode和Equals的关系是而且(&&)的关系。也就是说,只要GetHashCode和Equlas中有一个方法没有重写,在验证时没有重写的那个方法会调用基类的默认实现,而这两个方法的默认实现都是根据内存地址判断的,也就是说,其实一个方法的返回值永远会是false。其结果就是,存储的时候你可能任性的存,在取值的时候就是你哭着找不着娘了。
好了,说了这么多你应该对这两个方法有了从新的认识了吧。若是仍是不明白的话,用代码实现一下,保准明白。