CLR via C#学习笔记-第五章-对象相等性和同一性

5.3.2 对象相等性和同一性

开发人员经常写代码比较对象。例如,有时要将对象放到集合,写代码对集合中的对象排序、搜索或比较。

本节将讨论相等性和同一性谋害将讨论如何定义正确实现了对象相等性的类型。

System.Object的Equals方法的代码演示

System.Object类型提供了名为Equals的虚方法。作用是在两个对象包含相同值的前提下返回true。

Object的Equals方法是像下面这样实现的。

public class Object
{
    public virtual Boolean Equals(Object obj)
    {
        if(this==obj)return true;
        return false;
    }
}

乍一看,这似乎就是Equals的合理实现:加入this和obj实参引用同一个对象,就返回true。

似乎合理时因为Equals知道对象肯定包含和她自身一样的值。

但假如实参引用不同对象,Equals就不肯定对象是否包含相同的值,所以返回false。

换言之,对Object的Equals方法的默认实现,它实现的实际是同一性(identity),而非相等性(equalioy)。

遗憾的是Object的Equals方法的默认实现并不合理,而且永远都不应该这样实现。

研究一下类的继承层次结构,并思考如何正确重写Equals方法,马上会发现问题出在哪里。

正确实现Equals的要求

下面展示了Equals方法应该如何正确地实现。

1.如果obj实参为null,就返回false,因为调用非静态Euqals方法时,this所表示的当前对象显然不为null。

2.如果this和obj实参引用同一个对象时,就返回true。在比较包含大量字段的对象时,这有助于提升性能。

3.如果this和obj实参引用不同类型的对象,就返回false。

4.针对类型定义的每个实例字段,将this对象中的值与obj中的值进行比较,任何字段不相等,就返回false。

5.调用基类的Equals方法来比较它定义的任何字段。如果基类的Equals返回false就返回false,否则返回true。

正确实现Equals的代码演示

所以微软本应该像下面这样实现Object的Equals方法。

public class Object
{
    public virtual Boolean Equals(Object obj)
    {
        //要比较的对象不能为null
        if(obj==null)return true;
        //对象属于不同类型则肯定不相等
        if(this.GetType()!=obj.GetType())return false;
        //如果对象属于相同类型,那么他们的所有字段都匹配的前提下返回true
        //由于System.Object没有定义任何字段,所以字段是匹配的
        return false;
    }
}

但微软没有这样实现Equals,所以Equals的实现规则远比想象的复杂。

类型重写Equals方法时应调用其基类的Equals实现(除非基类就是Object)。

检查同一性的方法

另外,由于类型能重写Object的Equals方法,所以不能再用他测试同一性。

为了解决这个问题,Object提供了静态方法ReferenceEquals,其原型如下。

public class Object
{
    public virtual Boolean Equals(Object objA,Object objB)
    {
        return(objA==objB);
    }
}

检查同一性(看两个引用是否指向同一个对象)务必调用ReferenceEquals,不应使用C#的==操作符(储非先把两个操作数都转型为Object)。

因为某个操作数的类型可能重载了==操作符,为其赋予不同于“同一性”的语义。

可以看出在设计对象相等性和同一性时,.NET的设计很容易使人混淆。

System.ValueType就重写了Object的Equals方法,并进行了正确的实现来执行值的相等性检查。实现与上述示例相同,不做赘述。

在内部,ValueType的Equals方法利用反射完成上述步骤中的针对每个实例字段比较this和obj。

由于CLR反射机制慢,定义自己的值类型时应重写Equals方法来提供自己的实现。

自己的实现不调用base.Euqals。

自己重写Equals时的要求

定义自己的类型时,你重写的Equals要符合以下四个特征

1.Equals必须自反,x.Equals(x)肯定返回true。

2.Equals必须对称,x.Equals(y)和y.Equals(y)返回相同的值。

3.Equals必须可传递:x.Equals(y)返回true,y.Equals(z)返回true,则x.Equals(z)肯定返回true。

4.Equals必须一直。比较的两个值不变,Equals返回值也不能变。

重写Equals方法时,可能还需要做下面几件事

1.让类型实现System.IEquatable<T>接口的Equals方法。这个泛型接口允许定义类型安全的Equals方法。通常你实现的Equals方法应获取一个Object参数,以便在内部调用类型安全的Equals方法。

2.重载==和!=操作符方法。通常应实现这些操作符方法,在内部调用类型安全的Equals。

猜你喜欢

转载自www.cnblogs.com/errornull/p/9747108.html