`
dyllove98
  • 浏览: 1405307 次
  • 性别: Icon_minigender_1
  • 来自: 济南
博客专栏
73a48ce3-d397-3b94-9f5d-49eb2ab017ab
Eclipse Rcp/R...
浏览量:39053
4322ac12-0ba9-3ac3-a3cf-b2f587fdfd3f
项目管理checkList...
浏览量:80060
4fb6ad91-52a6-307a-9e4f-816b4a7ce416
哲理故事与管理之道
浏览量:133165
社区版块
存档分类
最新评论

了解Equals所发生的事情

 
阅读更多

首先,我推荐大家先看下Equals 和 == 的区别一文再继续往下看本文(虽然文中有些解释还是让人有点感觉不对),因为我就是从园友@一沐阳光的这篇文章中滋生出的问题,才写的本文。当然你也可以直接看本文,因为我并没有把本文与它强行联系在一起。

  先来看下面的代码:

复制代码
1             int i1 = 8;
2             int i2 = 8;
3             bool bo1 = i1 == i2;                    // true
4             bool bo2 = (object)i1 == (object)i2;    // false
5             bool bo3 = i1.Equals(i2);               // true
6             bool bo4 = i1.Equals((object)i2);       // true
7             bool bo5 = ((object)i1).Equals(i2);     // true
复制代码

  1、bo1=true这点是没有疑问的,值类型的比较,就是比较它的值。

 

 

  2、bo2=false。这是把两个int型的值类型装箱了,然后“==”比较时,是比较其引用的地址,所以为false。

  3、bo3=true。这是调用了int对应的Int32的Equals(int)方法:

1 public bool Equals(int obj)
2 {
3     return (this == obj);
4 }

  很显然,this就是i1,obj就是i2,最终返回的还是i1==i2,为true。

  4、bo4=true。可能我们经常都认为Equals(object)是继承自Object类:

1 public virtual bool Equals(object obj)
2 {
3     return RuntimeHelpers.Equals(this, obj);
4 }

  但经常忽略了一点,它是个virtual方法,像Int32、UInt32、Double、String等等常用类型,都是重写了这个方法的,而int对应的Int32类型重写为:

1 public override bool Equals(object obj)
2 {
3     return ((obj is int) && (this == ((int) obj)));
4 } 

  所以遇到i1.Equals(i2),首先调用了(obj is int)来判断i2是不是int型的,如果是就会有强制转化,实际上执行返回的是 i1==(int)((object)i2) ,可见,这必然也是true。

  其它类型的重写也与这类似,都会首先判断参数obj是不是当前类型,是则强制转化,再进行比较,如果不是,则看情况进行处理。这里说的看情况,主要包括了两种情况:第一种,本身是值类型。尝试转化参数的类型,如果参数与本身类型相同,这时直接调用==号进行比较(也有特殊情况),如果参数与本身类型不同,则返回false;第二种,本身是引用类型(注意string是引用类型),会尝试转化参数为本身的类型,失败则直接返回false,否则再比较引用的地址是否相同,进行返回。string是引用类型中的特例,不仅引用地址相同的string可以为真,内容相同的string比较也为真,可以看下面关于string类型的Equals(object)重写方法:

复制代码
 1 public override bool Equals(object obj)
 2 {
 3     if (this == null)
 4     {
 5         throw new NullReferenceException();
 6     }
 7     string strB = obj as string;
 8     if (strB == null)
 9     {
10         return false;
11     }
12     if (object.ReferenceEquals(this, obj))
13     {
14         return true;
15     }
16     if (this.Length != strB.Length)
17     {
18         return false;
19     }
20     return EqualsHelper(this, strB);
21 }
复制代码

  说明一下,最后的EqualsHelper(this,strB)函数内部就是对这两个字符串的内容进行逐字符地比较了。

  5、bo5=ture。这与前面的bo3、bo4并不一样了,这时编译器调用的不是Int32的Equals(int)或Equals(object)方法,而确实就是Object类型的Equals(object)方法,通过IL代码得知的,i1是通过(object)i1手动装箱的,而i2则是协变,自动完成装箱:

1   IL_0032:  ldloc.0                      // 读取i0
2   IL_0033:  box        [mscorlib]System.Int32     // 装箱
3   IL_0038:  ldloc.1                     // 读取i1
4   IL_0039:  box        [mscorlib]System.Int32         // 装箱
5   IL_003e:  callvirt   instance bool [mscorlib]System.Object::Equals(object)    // 调用Object.Equals(object)方法
6   IL_0043:  stloc.s    bo5                // 保存结果到bo5

  那么此时为什么又相等了,注意前面贴的关于Object的代码中,只有一行:RuntimeHelpers.Equals(this, obj);这句话我使用Reflector也没有找到具体实现,目测应该是用于帮助编译器实现运行时代码生成工作的,在其内部可能实现了转化,会在运行期根据i1的类型,再调用Int32的Equals方法来完成最终比较。为了验证我的想法,我在MSDN上找到了下面关于RuntimeHelpers.Equals(object, object)的话:

RuntimeHelpers 类

提供一组为编译器提供支持的静态方法和属性。无法继承此类。

RuntimeHelpers.Equals 方法 (Object, Object)

返回值

类型:System.Boolean

如果 o1 参数与 o2 参数是同一个实例,或者二者均为 null,或者 o1.Equals(o2) 返回 true,则为 true;否则为 false。

  由于(object)i1与(object)i2并不是同一个实例,二者又均不为null,所以进入i1.Equals(i2)的逻辑运算阶段,最终返回true。本文就此完结,这样一来,结合最开始贴出的文章,最少我自己已经能大概搞清楚实际发生了什么,而不需要去死记了,也不会在遇到这种情况时不知所措了。

  转载请注明原址:http://www.cnblogs.com/lekko/archive/2013/03/06/2946282.html 

分享到:
评论

相关推荐

    new concept 3

    ### 新概念英语第三册知识点分析 ...通过这两个章节的学习,我们不仅可以提高英语阅读能力,还能了解到不同文化背景下的故事和事件。这些故事不仅有趣,还能够帮助我们学习和运用英语语言中的各种语法结构和词汇表达。

    Java面试宝典9.0.pdf

    - **AIO**(Asynchronous IO):异步非阻塞IO模型,用户线程发起请求后可以立即返回去做其他事情,当后台处理完成之后再通知用户线程进行后续操作。 #### 1.14 ThreadLocal的原理(高薪常问) `ThreadLocal`是一个...

    新概念3笔记

    - **背景介绍**:介绍了古希腊的一个未被记载的女神,通过这个故事了解古希腊文化及其神话传说。 - **核心词汇**: - *goddess* (女神) - *unknown* (未知的) - **语法要点**: - 使用过去分词作为形容词。 - 定...

    《实现领域驱动设计》中的源码

    8. **领域事件(Domain Event)**:记录领域中发生的重要事情,可以用于触发异步操作或通知其他系统。 在`IDDD_Samples-master`这个压缩包中,你可能会看到如下结构: - **Infrastructure**:基础设施层,包含了...

    提高C#编程水平的50个要点(C#程序员必读)

    事件是一种特殊的委托,用于通知其他对象某件事情的发生。 #### 23. 避免返回类内部成员的引用 避免泄露内部实现细节,保护数据安全。 #### 24. 使用元数据来控制程序 元数据提供了一种在运行时获取类型信息的方式...

    2021_2022学年新教材高中英语UNIT5LANGUAGESAROUNDTHEWORLDSectionDReadingfor

    例如:“We depend on the newspapers for information about what is happening.”(我们依靠报纸获取关于正在发生的事情的信息。) - **not equal to**:搭配介词**to**,表示不等同于。例如:“Susan is not ...

    EffectiveC#中文版

    - 事件是通知其他对象某些事情发生的一种机制。 - 定义事件可以提供一种更清晰的方式来处理异步操作的结果。 23. **避免返回内部类对象的引用** - 返回内部类的引用可能会导致外部对象持有内部对象的引用,从而...

    Java开发规范(编程风格)

    - 遵循单一职责原则,确保类只做一件事情。 - 使用抽象类和接口来提高代码的复用性和灵活性。 **6.2 抽象类与接口** - 明确抽象类和接口的区别,合理使用它们。 **6.3 继承与组合** - 优先选择组合而非继承。 - ...

Global site tag (gtag.js) - Google Analytics