从《Microsoft.net框架程序设计》一书中,看到Equals的实现基本分为如下三类(顺序有所调整):(1)引用类型,从MyRefType到Object的继承链上(基类、基类的基类、...),有类覆盖了Object的Equals方法实现;(2)引用类型,从MyRefType到Object的继承链上(基类、基类的基类、...),均没有类覆盖Object的Equals方法实现;(3)值类型的Equals方法实现。分法相当科学,不过我看了其中的代码实现,针对第二种实现有一些自己的疑惑和想法。为了没有看过该书的同仁们更好地理解,我将书中的实现贴在此文中。先看书中第一种,(1)引用类型,从MyRefType到Object的继承链上(基类、基类的基类、...),有类覆盖了Object的Equals方法的实现:
其中用到了Object的Equals方法,将原代码贴出来
在这种实现中,个人认为Equals方法是没有问题的,首先调用
比较了基类的部分,然后就比较自己的部分就OK了。再看书中的第二种,2)引用类型,从MyRefType到Object的继承链上(基类、基类的基类、...),均没有类覆盖Object的Equals方法的实现;
我们可以发现,同第一种实现的差别在于没有了如下代码段:
为什么不能加这一段呢?因为在这种实现环境下,我们设定了前提:从MyRefType到Object的继承链上,均没有类覆盖Object的Equals方法。所以如果加了上面这段代码,那实际调用的是Object类的实例方法Equals
这样,如果this和obj不指向同一个对象,则这个base.Equals(obj)肯定返回false,于是Equals也返回false,而在this和obj指向同一个对象,Equals返回true,这样不就是Object的Equals()实现吗?既然如此,何苦自己重新这个函数呢?既然重写了,那肯定有不一样的行为,于是下面的代码段加入不得。
到这里,我们也可以理解。不过问题是,没有加入这段代码,从“(2)引用类型,从MyRefType到Object的继承链上(基类、基类的基类、...),均没有类覆盖Object的Equals方法的实现”中,我们好像找不到比较基类成员的影子。不比较基类成员,是否合理呢?看下面例子,如果两个Derived对象,无需比较Base部分的成员变量,那么下面的写法是正确的,此时该程序打印出的结果为True。
但是有时逻辑要求,两个Derived对象需要比较Base部分的成员变量,那上面的写法就是错误的,此时应该修改为如下的代码,该程序打印出的结果为False。
再看第三种实现:(3)值类型的Equals方法实现。书中代码如下:
这种实现方法,如果不存在其它类对MyValType的隐式类型转换,是没有问题的,如果存在隐式类型转换,那代码中的强类型Equals()方法(从第16行开始)就可能存在问题了。试想想,两种不同类型的实例,我们有多少场合会认为他们Equals呢?根据逻辑来定,一般不会认为它们相同。为了说明存在的这个问题,请看如下代码:
上面代码中,一般从常理来说,我们不会认为myValue1和myValue2相等,因为他们是不同类型的实例(myValue1属于MyValType类型,myValue2属于MyValType2类型)。但是实际输出了结果True。个中原因在于定义了一个从MyValType2到MyValType的隐式转换:
于是在运行语句myValue1.Equals(myValue2)时,会先将myValue2转换为一个MyValType类型的临时变量,然后用MyValue1和这个临时变量比较,此时调用强类型的比较函数,因为MyValType和MyValType2这两个类型内部结构一样,且两变量的内部field的值相同,所以返回True。上面例子说明 ,如果不能确保没有其他类型到该类型的隐式转换,那万无一失的办法就是不实现强类型的Equals。调用默认的Equals方法或者实现参数为Object的Equals方法,虽然效率可能差一点,但是可靠。当然,如果可以确认没有其他类型到该类型的隐式转换,那实现强类型的Equals方法还是可以带来效率的提升。(相同描述可参见《.net框架程序设计》)。关于Equals()方法的实现,基本上也就到此结束了。如果各位有什么好的心得,欢迎积极探讨。
posted on 2009-04-26 23:33 五味杂陈 阅读(1498) 评论(0) 编辑 收藏 引用 所属分类: .NET
Powered by: C++博客 Copyright © 五味杂陈