代码改变世界

一个简单判等例子的深度分析

2011-01-23 23:12  贼寇在何方  阅读(1664)  评论(1编辑  收藏  举报

今儿给网友讲解了一个判等的问题,看似简单,其实还是蛮有意思的:

object s = 1, t = 1;
Console.WriteLine(
"s == t -> {0}", s == t );
Console.WriteLine(
"s.Equals(t) -> {0}", s.Equals( t ) );

// 运行结果:
// s == t -> False
// s.Equals(t) -> True

这个结果可能会让不少同学觉得意外,那我就来给大家分析下

 


object s = 1, t = 1;

这里,我们看到给两个object变量做了初始化,其中伴随着装箱操作,

 

所谓装箱,简单地说,就是把值类型转换成引用类型的操作——object:引用类型,int:值类型

详细的可以看这篇文章:C# 装箱和拆箱

 

我们知道,对于值类型(Int32是值类型)的判等,是二进制的按位判等; 而对引用类型(object是引用类型)的判等,默认为判断两者内存地址是否一致。

这里的object对象,s和t分别初始化,所以地址定然不一致

可以是用object.ReferenceEquals方法验证一下:

 

Console.WriteLine( "ReferenceEquals(s, t) -> {0}"object.ReferenceEquals( s, t ) );

// 运行结果:
// ReferenceEquals(s, t) -> False

 

 

结果得到证实,所以 s == t自然就False了

 

 

相比而言,后面的表达式s.Equals(t)就会复杂一些

我查看了object.Equals( object )这个方法的代码(用的Reflector):

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

 

再去查看了RuntimeHelpers.Equals( object, object )的代码,最后发现这是个CLR实现的方法,没有代码···

仔细想想,关键不在RuntimeHelpers.Equals, 而在这个virtual

对了,这是个虚方法,Int32类型必然会重写这个Equals方法。也就是说,在程序中,实际调用的应该是Int32.Equals

 

再看Int32的Equals方法,结果就明朗了

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

 

在这里,s和t都被转换成int类型,再做值类型的按位比较,结果当然是1等于1,返回True

 

参考: