首页 > 代码库 > 在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方法,实现了内容的比较。