首页 > 代码库 > 在System.Object中定义的三个比较方法有何异同

在System.Object中定义的三个比较方法有何异同

分析问题

  在本书前面的章节中,笔者已经介绍过System.Object中的所有方法。而其中有三个方法被设计用来进行对象的比较,它们是:

  (1)、static bool ReferenceEquals(object A,object B)。

  (2)、static bool Equals(object A, object B)。

  (3)、virtual bool Equals(object obj)。

  1、静态方法:ReferenceEquals实现了类型的引用比较,从参数类型可以看出,它不仅可以用来比较两个引用类型对象,也可以用来比较两个值类型对象。当然,根据值类型和引用类型的特点,ReferenceEquals方法只有应用在引用类型上时才会有意义,比较两个值类型的引用将永远返回false,无论它们的值是否相等。设置于当下面的代码被执行时:

  

int i=0;Console.WriteLine(object.ReferenceEquals(i,i));

  得到的结果将仍然是false,读者可以尝试从装箱拆箱的原理去思考原因。

  2、静态方法:Equals是另外一个静态比较方法,它实现的功能根据不同的类型而有所不同。事实上,Equals方法的功能依靠了实例Equals方法的实现,概况地讲,静态Equals方法的内容分为两步:首先检查两个对象是否恒等(==),然后调用其中一个参数对象的实例Equals方法来判断两个对象是否恒等。

注意

  对于值类型和引用类型来说,检查两个对象是否恒等(==)的概念是不同的。对于引用类型来说,这样的检查是指是否引用相等,而对于值类型来说,检查是指针对内容进行的。

  利用发射器,可以清楚地看到静态方法Equals的代码:

public static bool Equals(object objA,object objB){  return ((objA==objB)||(((objA!=null)&&(objB!=null))&&(objA.Equals(objB)));}

  正如笔者前面分析的,Equals方法首先使用==运算符来检查参数对象是否相等,然后检查空引用的情况并且调用实例方法Equals进行比较。这样实现的功能完全依赖于实例方法Equals的实现,所以读者将不需要隐藏静态的Equals方法,而应该直接把注意力放在实例方法Equals上。

  3、实例方法:Equals实际上承担了大部分的比较工作,它是一个虚的实例方法,在用户需要构建自定义比较时,就需要从写Equals方法。下面是mscorlib.dll的中间代码,它展示了Object中Equals方法的默认实现:

  

  看上去十分简单,调用了InternalEquals方法来检查两个对象的内存地址是否相等,也就是说,Equals的默认实现就是引用相等的比较。笔者在前面章节中已经介绍了,引用比较在某些场合下并不实用,而内容比较可能更加符合业务逻辑的需求,而这个时候,就需要程序员为自己的类型从写实例Equals方法。事实上,所有值类型的基类ValueType已经重写了实例Equals方法。以下代码是一个重写的例子。

  

using System;namespace TestOverrideEquals{    class OverrideEquals    {        static void Main()        {            MyObject o1 = new MyObject(10, "我是字符串");            MyObject o2 = new MyObject(10, "我是字符串");            //打印引用比较结果            Console.WriteLine("引用比较:{0}", object.ReferenceEquals(o1, o2).ToString());            //打印内容比较结果            Console.WriteLine("自定义的内容比较:{0}", o1.Equals(o2).ToString());            Console.Read();        }    }    class MyObject    {        private int _MyInt;        private string _MyString;        public MyObject() { }        public MyObject(int i, string s)        {            _MyInt = i;            _MyString = s;        }        public override bool Equals(object obj)        {            //检查空引用            if (obj==null)            {                return false;            }            //做引用比较,如何两者引用相等,势必内容相等            if (object.ReferenceEquals(this,obj))            {                return true;            }            //检查两者的类型是否相等            //考虑到继承的影响,运行时的类型未必是MyObject            //所以使用反射得到确切的类型,牺牲性能来保证正确性            if (this.GetType()!=obj.GetType())            {                return false;            }            //实现内容比较            MyObject right = obj as MyObject;                        if (_MyInt==right._MyInt&&                //String类型虽然是引用类型,但实现了内容比较的==                _MyString==right._MyString)            {                return true;            }            return false;                    }    }}

  在以上代码中,MyObject重写了System.Object的实例Equals方法,实现了内容上的比较。执行程序可以得到这样的结果:

  

注意

  以上代码的编译会产生一个警告,这是因为MyObject从写了Equals方法,而没有重写GetHashCode方法,这样做会产生潜在的隐患,这一点将在后续章节中有所覆盖。

答案

  静态方法ReferenceEquals实现了引用比较。静态Equals方法实现了比较高效地调用实例Equals方法的功能。实例Equals方法是一个虚方法,默认的实现是引用类型,类型可以根据需要重写实例Equals方法。值类型的基类ValueType重写了Equals方法,实现了内容的比较。