对象的等值性与惟一性
如前所述,System.Obiect类型提供了一个名为Equals 的虚方法,其目的为判断两个对象是否有着相同的“值”。微软的.NET框架类库(FCL)中包括的许多方法(例如 System.Array的IndexOf方法System.Collections.ArrayList的 Contains 方法)在内部都调用到了 Equals方法。因为 Equals 方法定义在Obiect中,而每个类型最终都派生自Obiect,所以我们可以保证每个类型的实例都有一个这样的 Equals方法。对于那些没有显式重写Equals方法的类型,Obiect(或者重写了Eauals方法的最近的那个基类) 提供的实现将被继承。下面的代码展示了System.Obiect 类型中的Equals 方法实现:
class object {public virtual Boolean Equals(0bject obj){//如果两个引用指向的是同一个对象//它们肯定相等if (this == obj)return(true);//假定两个对象不相等return(false);}...
}
如我们所见,该方法采取的策略可能是最简单的:如果进行比较的两个引用指向的是同一个对象方法将返回true;否则在任何其他情况下,方法都将返回false。如果我们定义了自己的类型,并且希望比较它们中的字段是否相等,Object类型提供的默认实现对我们来说是不够的,我们必须重写Equals方法,提供自己的实现。
当实现自己的Equals方法时,我们必须确保它遵循以下4条规则:
- Equals 方法必须是自反的,也就是说,x.Equals(x)必须返回true。
- Equals 方法必须是对称的,也就是说,x.Equals(y)和y.Equals(x)必须返回同样的值。.
- Equals 方法必须是可传递的,也就是说,如果x.Equals(y)和y.Equals(z)都返回 true,那么x.Equals(z)也必须返回 true。
- Equals方法必须是前后一致的,也就是说,如果两个对象的值没有发生改变,多次调用Equals力法的返回值应该相同。
如果我们为Eguals方法提供的实现没有遵循上述4条规则,我们的应用程序将会发生一些奇怪的不可预期的行为。
不幸的是,重写 Obiect 的 Equals方法并不如想象的那么容易。我们必须执行许多步操作,并且要保证每一步操作都是正确的。另外,根据我们定义的类型的不同,这些操作也会有一些差别。所幸的是,实现Eguals方法只有3种不同的方式。下面我们逐一讨论每一种模式。
为基类没有重写 Obiect.Eauals 方法的引用类型实现 Eauals
对于那些直接继承了Obiect的 Equals实现的类型,下面的代码展示了怎样为它们实现 Equals方法:
//这是一个引用类型('class'的缘故)class MyRefType : Baserype{RefType refobj; //该字段是一个引用类型Valrype valobj; //该字段是一个值类型pubiic override Boolean quals(0bject obj){//因为'this'不为null,所以如果obj 为 null,//那么两个对象将不可能相等if (obj == null) return false;//如果两个对象的类型不同,那么它们不可能相等if(this.GetType()!= obj.GetType())return false;//将ob〕转型为定义的类型以访问其中的字段。注意这里的转型不会失败,因为已经知道两个对象是同一个类型1/MyRefType other = (MyRefrype)obj;//比较其中的引用类型字段if (!0bject.Equals(refobj, other.refobj))return false;//比较其中的值类型字段if (!valobj,Equals(other.valobj))return false;return true;//到这里两个对象才算相等}//重载==和!=操作符(可选)public static Boolean operator ==(MyRefrype ol, MyRefrype o2){return object.Equals(ol,o2);}public static Boolean operator !=(MyRefrype ol, MyRefType o2){return !(o1 == 02);}}
这里实现的 Eguals首先将 obi和 nu相比较。如果被比较的对象不为 null,那么接着比较两个对象的类型。如果两个对象的类型不同,那么它们不可能相等。如果两个对象有着相同的类型,就将obi 转型为 MyRefType,这里的转型不可能抛出异常,因为我们已经知道两个对象为同一个类型。等上述所有步骤都正确执行完毕后,我们才开始比较两个对象中的字段。如果两个对象中所有的字段都相等,方法将返回 true。
在比较两个对象中的字段时,我们必须非常仔细。前面的代码展示了根据字段类型的不同,所进行的两种不同的比较方式。
比较引用类型的字段
要比较引用类型的字段,我们应该调用Obiect的静态Equals 方法。Object 的静态 Equals方法是一个比较两个引用类型对象的辅助方法。下面展示了 Object 的静态Eguals方法的内部实现:
public static Boolean Equals(0bject objA, object objB){// 如果 objA 和 objB 指向的是同--个对象,方法返回 trueif (objA == objB)return true;// 如果 objA 或者 objB 为nu1l,它们不可能相等,方法返回 falseif ((objA == null)|l(objB == null))return f