对象的相等性是指两个对象在逻辑上是等价的,而对象的唯一性是指两个对象引用在应用程序域中是否指向同一存储结构。
实际上与其说是对象的唯一性,不如说是变量的唯一性。因为唯一性判定和对象引用有关而和对象本身无关。
变量的唯一性通过按位比较进行,所有基本类型的唯一性判定都是通过这种方式进行的。两个Object类型的变量相等当且仅当保存的位模式相同,这和比较两个int没什么不同。一个隐含的前提是托管堆中的不同对象的引用值必然不同,相同对象的引用值必然相同。
对象的相等性是一种逻辑上的概念。其实现必须满足等价关系,自反、交换、传递。并且如果两个对象的状态没有发生变化,用Equals进行判定的结果也不能变化。
实现Equals方法是一件比较麻烦的事,同时还要实现GetHashCode方法。基本上GetHashCode的返回值是对象状态的摘要,因此对象状态相同,返回的摘要也必须相同。但是不同的两个状态也有可能返回相同的摘要,因为是摘要...。这么看来,Equals和GetHashCode都是和一种叫做对象状态的东西有关。
之所以说GetHashCode基本上是对象状态的摘要,是因为GetHashCode也不完全是对象状态的摘要。提到摘要,就要考虑如何根据给定的状态信息计算摘要,也就是摘要的计算方法。FCL认为,GetHashCode方法返回的应当是在程序的一次执行过程中对象状态的摘要。因此,同样的对象状态在不同的进程中可能会计算出不同的HashCode。另外随着程序版本的变化,GetHashCode的算法是允许变化的。
重要的结论是,不要把HashCode当作对象状态的散列码用。因为对于给定的对象状态和散列算法,散列码总是相同的;但是对于给定的对象状态和Hash算法,仍然允许HashCode随应用程序域的变化而不同。千万不要持久化对象的散列码!
实现Equals最好还要同时重载==和!=运算符。对于值类型,最好还要实现一个强类型版本的Equals。
最后补充一点,Equals的默认实现是,对于引用类型是相同引用(指向同一对象)算是相等;对于值类型是相同位模式算是相等。相对应的,对于引用类型,GetHashCode的默认实现据说是引用本身,也就是托管堆的TrackHandle值。我觉得不管到底是不是这样,为了思考上的便利可以做这个假定。这个说法是符合上面那一堆约束的。这里只讨论引用类型。TrackHandle的特点是同一个对象在不同应用程序域中的TrackHandle的值可能不同,这点和HashCode相同。在AppDomain内,同一个对象的TrackHandle是不会变的,同一个对象的默认HashCode也是不会变的。而且HashCode的默认实现是和对象状态无关的,由于Equals的默认实现也和对象状态无关,因此满足Equals相等,返回的HashCode也相等。在默认实现中认为,对象的状态集合为空,有待用户自己去实现。
参考:《框架程序设计》