Microsoft.Net框架程序设计学习笔记(7):重写Object.Equals方法
时间:2011-03-25 来源:辛勤的代码工
System.Object类型提供了名为Equals的虚方法,目的为判断两个对象是否有相同的“值”。.Net框架类库(FCL)中许多方法在内部都调用了Equals方法(如:Array的IndexOf方法、ArrayList的Contains方法)。对于没有显式重写Equals的类型,Object(或重写了Equals方法的最近的那基类)提供的实现将被继承。以下代码展示了System.Object类型中的Equals方法实现:
class object
{
public virtual Boolean Equals(object obj)
{
//如果引用指向的是同一对象,肯定相等
if (this == obj) return true;
return false;
}
//......
}
该方法采取的策略可能是最简单的:如果进行比较的两个引用指向的是同一个对象,方法将返回true;否则在任何其他情况下,方法都将返回false。如果我们定义了自己的类型,且希望比较它们中的字段是否相等,则Object类型提供的默认实现对我们来说是不够的,必须重写Equals方法。
实现Equals方法有3种不同方式,我们逐一讨论。
- 为基类没有重写Object.Equals方法的引用类型实现Equals
class MyRefType : BaseType
在比较两个对象中的字段时,我们必须非常仔细。根据字段类型不同,比较方式亦不同。
{
//引用类型字段
RefType refobj;
//值类型字段
ValType valobj;
public override bool Equals(object obj)
{
//如果obj为null,不可能相等
if (obj == null) return false;
//如果两个对象类型不同,不可能相等
if (this.GetType() != obj.GetType()) return false; MyRefType other = (MyRefType)obj;
//比较其中的引用类型字段
if (!object.Equals(refobj, other.refobj)) return false;
//比较其中的值类型字段
if (!valobj.Equals(other.valobj)) return false;
return true;
}
//重载==和!=操作符
public static Boolean operator ==(MyRefType o1, MyRefType o2)
{
return object.Equals(o1, o2);
}
public static Boolean operator !=(MyRefType o1, MyRefType o2)
{
return !(o1 == o2);
}
}
比较引用类型字段:
比较引用类型字段时,我们应该调用object的静态Equals方法,该方法是比较两个引用类型对象的辅助方法,以下代码展示了该方法的内部实现:public static Boolean Equals(Object objA, Object objB)
比较值类型字段:
{
//如果objA与objB指向同一个对象,返回true
if (objA == objB) return true;
//如果objA或objB为null,不可能相等
if ((objA == null) || (objB == null)) return false;
//判断objA与objB是否相等
return objA.Equals(objB);
}
比较值类型字段,我们应该调用该字段类型的Equals方法来比较它们。不应该调用Object的静态Equals方法,因为值类型对象的值永远不可能为null,且调用object的静态Equals方法会对值类型对象执行装箱操作。 - 为基类重写了Object.Equals方法的引用类型实现Euqals
class MyRefType : BaseType
{
//引用类型字段
RefType refobj;
//值类型字段
ValType valobj;
public override bool Equals(object obj)
{
//首先让基类比较其中的字段
if (!base.Equals(obj)) return false;//以下代码与上一节中的实现相同
//如果obj为null,不可能相等
if (obj == null) return false;
//如果两个对象类型不同,不可能相等
if (this.GetType() != obj.GetType()) return false;MyRefType other = (MyRefType)obj;
//比较其中的引用类型字段
if (!object.Equals(refobj, other.refobj)) return false;
//比较其中的值类型字段
if (!valobj.Equals(other.valobj)) return false;
return true;
}
//重载==和!=操作符
public static Boolean operator ==(MyRefType o1, MyRefType o2)
{
return object.Equals(o1, o2);
}
public static Boolean operator !=(MyRefType o1, MyRefType o2)
{
return !(o1 == o2);
}}
这段代码和前一节中展示的代码大体一样,唯一差别是这里还要求比较基类型中定义的字段。
注意:如果调用base.Equals会导致调用object.Equals方法,那就不应该调用它。 - 为值类型实现Equals方法
所有的值类型都继承自System.ValueType。ValueType重写了Object的Equals方法实现。ValueType.Equals方法在内部首先使用反射机制来得到类型所有的实例字段,然后再比较它们是否相等。这种方法效率很低,但却是一个所有值类型都能继承的默认实现。下面的代码展示了System.ValueType.Equals方法的内部实现:class ValueType
该方法已经提供了一个相当好的Equals实现,书中虽然介绍了自己提供强类型版本的Equals实现,但在该值类型与其他值类型之间存在隐式转换时,并不是一个好方法,下面仅列出代码供参考:
{
public override bool Equals(object obj)
{
if (obj == null) return false;
//得到this的类型
Type thisType = this.GetType();
if (thisType != obj.GetType()) return false;
//取得该类型的所有公有和私有实例字段
FieldInfo[] fields = thisType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
for (int i = 0; i < fields.Length; i++)
{
//从两个对象中得到字段的值
object thisValue = fields[i].GetValue(this);
object thatValue = fields[i].GetValue(obj);
//如果字段值不等,返回false
if (!object.Equals(thisValue, thatValue)) return false;
}
return true;
}
//......
}struct MyValType
{
RefType refobj;
ValType valobj;
public override bool Equals(object obj)
{
if (!(obj is MyValType)) return false;
return this.Equals((MyValType)obj);
}
//强类型版本Equals
public Boolean Equals(MyValType obj)
{
if (!object.Equals(this.refobj, obj.refobj)) return false;
if (!this.valobj.Equals(obj.valobj)) return false;
return true;
}
//重载==操作符
public static Boolean operator ==(MyValType v1, MyValType v2)
{
return v1.Equals(v2);
}
public static Boolean operator !=(MyValType v1, MyValType v2)
{
return !(v1 == v2);
}
}
题外话:关于Object.ReferenceEquals静态方法
Equals方法的目的是比较两个类型实例是否相等,如两个实例有相同的状态或值,则返回true。然而,有时我们需要判断两个引用是否指向了同一个对象,为实现这个功能,Object提供了一个名为ReferenceEquals的静态方法,它的实现如下:
class object
{
public static Boolean ReferenceEquals(Object objA, Object objB)
{
return objA == objB;
}
}
使用示例:
ClassA obj1 = new ClassA();
ClassA obj2 = obj1;
Console.WriteLine(Object.ReferenceEquals(obj1, obj2));
ClassA obj2 = new ClassA();
Console.WriteLine(Object.ReferenceEquals(obj1, obj2));
相关阅读 更多 +