博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

漫谈.Net中对象相等

Posted on 2011-06-24 12:20  gczhao  阅读(142)  评论(0编辑  收藏  举报

本文转自:http://www.cnblogs.com/XmNotes/archive/2011/06/23/2088341.html

 

.Net中对象之间的相等比较,是看似简单,实际上有点儿复杂。这和现实世界的情况差不多,无论人或物,现实中没有两个绝对相等,只有相对的属性一致或同属某个类别,这学问细究下去无穷无尽,一辈子也未必参得透。而.Net中的相等,没有那么捉摸不透,却也值得品味一番。

  说到相等,新手上来,先学到的就是相等操作符==(有的.Net语言中是单=),这个很自然,问题是有不少人工作了一两年,提到相等还是只想到操作符,就太片面了。

  在这里,茴香豆的茴字有四种写法,.Net中也主要有四种相等比较,分别是:==操作符Object.Equals方法Object.ReferenceEquals方法对象实例的Equals方法

  先来看Object的两个静态方法,它们逻辑比较简单。ReferenceEquals方法是比较两个对象的引用是否相同,即栈上的地址是否一样,对于值类型没有意义,参数中若有值类型参数出现,必定返回false。它主要用来测试,实际应用开发中很少用到,方法名也有点长。对于引用类型,如果方法结果为True,这个相等是最严格、最纯粹、如假包换的相等,说明这两个参数其实是同一个对象,当然无论用其他哪种相等比较方式,同样也应返回True。

  Object的Equals静态方法实际上是对实例Equals方法的扩展,增加了null的判断,适应于比较两个可能为空引用的对象。对于值类型,和Equals实例方法功能完全一样。

  再来看==,我们天天打交道的这小小操作符并不那么简单。上面,我们说两个Object静态方法区别在值类型和引用类型上,对于其他相等比较区别也主要在此。一般情况下,不是所有,对于引用类型==和ReferenceEquals静态方法作用相同;值类型在这里则有区分,对于原生值类型,如int,double,long,char等,==是直接比较其数值,而且不同类型间可以互相比较,比如int和char,'A’==65返回的是True;而对于一般的Struct,如果没有在代码中定义==(也包括!=)操作符,是不能用==比较的。

  引用类型也可以定义==操作符,覆盖CLR原生支持的比较。最常见的是String类型,它就定义了==操作符,很合理地放宽了相等的条件,使得String类型像原生值类型一样按值比较。String类的==操作符其实就是直接调用的被自己重写过Equals方法。

  String类是最常用也最特别的一个类,大部分面试都会问到String的特点,除了不可变和内存驻留机制外,其他主要特点就是相等的特殊性了。

  最后就来说说实例Equals方法吧,这是个Virtual方法,是我们在应用开发中,经常要根据业务逻辑需要,进行覆写的方法。定义并使用操作符固然方便,不过除了像String之类的特殊情况,引用类型让==保持默认规则是更好的选择,而让Equals方法实现业务上的“值”相等。如果不覆写,Equals方法也是比较对象的引用。

  对于值类型,实现==操作像一个点缀,而如果想实现相等比较操作,应该优先重写Equals方法(同样若要实现大小比较,应该优先实现IComparable接口,而不是实现比较操作符),从Object继承的Equals方法用于值类型时,比较两个对象的所有字段,全相等才为True。要注意它据说用了反射,效率很低的。但是它低归低,为什么一定要优先重写它?

  因为所有.Net Framework键值集合,都是用Equals实例方法做比较的,所以它实际上成了.Net中的法定天平,无论是原生类型、结构或类的实例,都应以Equals方法作为其标准的相等比较方式,包括我们自己实现的类型。用实例方法的好处也可以理解,更灵活,我们可以添加一些重载的Equals方法,申明不同的比较前提条件。与重写的默认Equals方法配合,构成一套完整的比较规则,以符合现实里复杂多变的标准。

  重写Equals方法时,官方推荐重写GetHashCode方法,要是你不用此类型作键值集合键的话,其实无所谓。

  个别情况中,复杂到重载Equals方法也力不从心时,我们就要定义专门用来比较相等的功能类。.Net Framework已经提供了一个接口System.Collections.IEqualityComparer,并有几个内置的实现,如StringComparer、EqualityComparer,我们自己写的比较类也不妨实现这个接口,当然,只要能用也不必计较那么多。看.Net Framework源代码,能发现好多个乱七八糟的类用于比较相等,大概是内部特权吧。

  结尾外,总结一张表,可以一目了然:

方式  

类别 

类默认行为 

原生 

 

不同类型可否相 

为是否能覆

应用场

Object.Equals

静态方法

调用Equals实例方法

值相等

各字段值相等

参数可能为Null的情况

Object.ReferenceEquals

静态方法

引用相等

返回False

返回False

不可

不可

测试

 ==

操作符

引用相等

值相等

须定义后使用

不可

原生类型,简单业务逻辑

Equals

实例方法

引用相等

值和类型相等

各字段值相等

一般处理