在System.Object类中有三个被设计用来进行对象比较的方法:
public virtual bool Equals(Object obj);
public static bool Equals(Object objA,Object objB);
public static bool ReferenceEquals(Object objA,Object objB);
第一个是virtual 的,后两个是 static 的。ReferenceEquals() 实现了类型的引用比较,对该函数来讲,只有应用在引用类型上时才会有意义,比较两个值类型的引用将永远返回 false ,
无论它们的值是否相等。甚至下面的代码:
int i = 0;
Console.WriteLine(object.ReferenceEquals(i, i));
输出结果也是 false . 原因是:i 是值类型的int .在传入时被自动装箱为object型变量,并且装了两次,那就是两个不同的对象啦,因此引用不相等(原因是我猜测的,存疑!)
另一个静态的 Equals() 方法的功能根据不同的类型而有所不同。它的内部判断实际分了两步:首先检查两个对象是否恒等(对于值类型来说,就是检查值内容是否相等;对于引用类型来说,就是检查引用是否相等)。然后调用其中一个参数对象的实例的Equals方法来判断两个对象是否恒等。
利用反射可以看到 静态方法 Equals() 的代码:
public static bool Equals(object objA,object objB)
{
return ((objA == objB || (((objA != null && objB != null && objA.Equals(objB)));
}
注意这里面只有一行代码,却是别有意味。它通过一个 || 运算符,把两种情况,值类型,跟引用类型,都包含了进去。一一分析:
如果objA , objB 皆为 值类型,则在 objA == objB 这一步骤无论如何都要得到一个false ,理由同前,因为它们要被装箱。实验如下:
int ia = 5;
int ib = 5;
object oa = ia; //这儿手动装下箱。
object ob = ib;
Console.WriteLine(oa == ob); // 结果是 false!!!
这时候后备的半句就用上了,
首先用 objA != null && objB != null 确保两个参数皆不为null ,然后调用了值类型本身的Equals 方法,这时候要明白,所有的值类型都继承自 System.ValueType 类,而ValueType 类复写了
public virtual bool Equals(Object obj);方法,此时的Equals(Object obj)就可以进行值类型的恒等判断了,比较的是里面存放的内容,而非引用,这时候就能得到正确的结果了。验证如下:
int ia = 5;
int ib = 5;
object oa = ia;
object ob = ib;
Console.WriteLine(oa == ob); // 结果是 false!!!
Console.WriteLine(oa.Equals(ob)); // 结果是 true !!!!!
前面我们假设的是两个值类型的情况,可以正常满足判断功能。当两个参数皆为引用类型的时候,objA == objB 这前半个条件已经够用了。当两个参数一个为值类型一个为引用类型,那就显然不用说,也会得到false 啦。。。
最后还有一个virtual 的 Equals(object obj) 方法,当我们需要构建自定义的比较时,就需要重写该方法,它的默认实现是大多情况下并不实用的引用比较,当然System.ValueType已经复写了该方法。所以系统定义的那些值类型可以放心使用,它们会正常比较值的内容的。
下面部分转自:在路上
重写Equals方法分为以下三种情况:
1.引用类型的基类没有重写Equals
2.引用类型的基类重写了Equals
3.值类型重写了Equals
通常写法是:
对象是否为空,为空返回false
对象类型是否相同,不同返回false
类型转换后进行引用与值的比较如下:
Object.Equals(v1,v2) ,不同返回false
v1.Equals(v2),不同返回false
对于基类,if(!base.Equals(v2)) return false;
值类型可以自己实现强类型的Equals,避免拆箱与装箱操作.注意System.ValueType.Equals()利用了反射实现,效率不高.
注意:Object.ReferenceEquals();
重写Equals必须重写GetHashCode()