对象判等
基本概念:
- 值相等——表示比较的两个对象的数据成员按内存位分别相等,即两个对象类型相同,并且具有相等和相同的字段。
- 引用相等——表示两个引用指向同一对象实例,也就是同一内存地址。因此可以由引用相等推出其值相等,反之则不然。
object类中和判等相关的方法及其代码:
// Returns a boolean indicating if the passed in object obj is // Equal to this. Equality is defined as object equality for reference // types and bitwise equality for value types using a loader trick to // replace Equals with EqualsValue for value types). // public virtual bool Equals(Object obj) { return InternalEquals(this, obj); } [MethodImplAttribute(MethodImplOptions.InternalCall)] internal static extern bool InternalEquals(Object objA, Object objB); public static bool Equals(Object objA, Object objB) { if (objA == objB) { return true; } if (objA == null || objB == null) { return false; } return objA.Equals(objB); } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] public static bool ReferenceEquals(Object objA, Object objB) { return objA == objB; }
一、public virtual bool Equals(Object obj) ,虚方法,比较对象实例是否相等。
- 其行为和ReferenceEquals()一致,判断的是“引用相等”。但是值类型例外,System.ValueType重写了Object.Equals()方法,比较类型和内容。ValueType中的实现性能并不好,因为它是所有值类型的基类,为了提供正确的行为,它必须能够在不知道对象运行时类型的情况下,比较其派生类型中的所有成员变量,在C#中,这就意味着要使用反射。因此我们应该为自己的值类型提供一个更快的Equals()重写版本。
- 对于引用类型,只有当我们希望更改其预定义语义时,才应该重写Equals()虚方法,例如:string。
- 当我们重写Equals()虚方法时,如果基类的Equals()方法不是由System.Object或System.ValueType提供的话,我们也应该调用基类的Equals()方法。
二、public static bool Equals(Object objA, Object objB) ,静态方法,比较对象实例是否相等。
三、public static bool ReferenceEquals (Object objA, Object objB),静态方法,比较两个引用是否指向同一对象(.NET认为null等于null)。
- 如果我们使用ReferenceEquals()来比较两个值类型,其结果永远返回false;即使我们将一个值类型和自身进行比较,返回值仍然是false。
四、“==”操作符在值类型情况下表示是否值相等,由值类型的根类System.ValueType提供了实现;而在引用类型情况下表示是否引用相等;“!=”相类似。
- 只要我们创建值类型,都需要重新定义operator==()。其理由和重写ValueType.Equals()的理由一样。
- 创建引用类型的时候,很少需要重写operator==()。因为它遵守“引用语义”。
下面是一个标准的Equals()虚方法的重写模式:
public override bool Equals(Object right) { //检查是否为null //在C#方法中,this指针永远都不可能为null if (right == null) return false; //检查是否引用相等 if (object.ReferenceEquals(this, right)) return true; //检查类型 if (this.GetType() != right.GetType()) return false; return ComparerSampleMembers(); }在检查类型的时候,也许有人会问说,类型兼容就可以了,为什么非要一致呢,于是就有了下面的实现:
//检查类型兼容性 Sample obj = right as Sample; if (obj == null) return false;我们要牢记相等的数学属性:自反、对称、可传递。如果我们以上面的代码实现判等,则不满足对称性,很可能出现那种A==B,B!=A的情况,这是诡异,无法接受的。