• 四种方式话Equal


    Item 9: Understand the Relationships Among ReferenceEquals(), static Equals(), instance Equals(), and operator== 读后感


    C#提供了四种相等比较的函数.

    public static bool ReferenceEquals( object left, object right );
    public static bool Equals ( object left, object right );
    public virtual bool Equals( object right);
    public static bool operator==( MyClass left, MyClass right );

    上述四个方法override应该遵循如下规则:

    1.前两个无论如何不要override

    2.第三个和第四个在自定义类型是ValueType的时候要改写

    3.当自定义类型是ReferenceType的时候,如果想改变RefrenceType默认的用对象标志判等的方式,可以改写第三个

    4.当自定义类型是RefrenceType的时候,最好不要改写operator==.

    下面分别给出上述规则的原因:

    1.

     A.ReferenceEquals()这个方法当且仅当两个变量指向同一对象的时候才相等,它的实现是比较对象标志(Object Identity,任何对象在创建的时候都会生成一个OI),所以任何值类型用ReferenceEquals()比较所得到的结果都是false,即使跟自己比较也是,因为要装箱,装箱的结果肯定不是同一个对象.所以这个方法也就没有改写的必要.object的ReferenceEquals()是实现这种比较最好的方式.

     B.Equals (object left, object right )这个方法用于在不知道待比较的参数是何种类型的时候.那如何比较两个变量是否相等那?很简单,把比较相等的职责交给其中一个待比较变量的Equals方法,上述方法的实现大概如下所述:

    public static bool Equals( object left, object right )
    {
      // Check object identity
      if (left == right )
        return true;
      // both null references handled above
      if ((left == null) || (right == null))
        return false;
      return left.Equals (right);
    }
    

      之所以不要重写上述两个方法,是因为两者都很好的实现了各自的语意.

    2.为什么是值的时候要重写第三个方法?

    ValueType是所有值类型的基类,由于要实现值相等的语意,它重写了object的Equals方法,但是在不知道派生类变量和其类型的情况下,ValueType的Equals在实现的时候只能用反射来比较,这样的效率是很低的,所以任何自定义的值类型都要重写Equals,以避免用ValueType的默认实现带来的反射的效率损失.

    3.object的实例Equals方法的默认实现跟ReferenceEqual一样,是通过比较对象标志来实现的,有些ReferenceType需要值相等的语意,比如string,这是就要改写实例Equals.

    4..Net FrameWork期望所有的ReferenceType的operator==保留引用语意.

    OK,终于弄完了,最后说一下文章中介绍的如何改写实例Equals方法.

    相等在语意上必须满足三点

    1.自反       a=a必须永远成立;

    2.对称       a=b则b=a;

    3.传递       a=b;b=c则a=c

    public class Foo
    {
      public override bool Equals( object right )
      {
        //1.判断待比较变量引用是否为空
        if (right == null)
          return false;
     //2.是否指向同一实例,如果是同一实例则必然相等.
        if (object.ReferenceEquals( this, right ))
          return true;

        //3. 判断类型是否相同
        if (this.GetType() != right.GetType())
          return false;

        // 4.比较内容
        return CompareFooMembers(
          this, right as Foo );
      }
    }
    有个看官可能要说了,为什么不判断this的引用是否为空?得,this为空,在进入Equals方法前就报那个经典的"未....."错误了,还等到这个方法里判断嘛.

    那位又说了,1,2,4没异议,可第三步如果可以转化成this引用的对象不行吗?

    答案是不行,必须是类型相同的.举例如下:

    public class B
    {
      public override bool Equals( object right )
      {
        // check null:
        if (right == null)
          return false;

        // Check reference equality:
        if (object.ReferenceEquals( this, right ))
          return true;

        // Problems here, discussed below.
        B rightAsB = right as B;
        if (rightAsB == null)
          return false;

        return CompareBMembers( this, rightAsB );
      }
    }

    public class D : B
    {
      // etc.
      public override bool Equals( object right )
      {
        // check null:
        if (right == null)
          return false;

        if (object.ReferenceEquals( this, right ))
          return true;

        // Problems here.
        D rightAsD = right as D;
        if (rightAsD == null)
          return false;

        if (base.Equals( rightAsD ) == false)
          return false;

        return CompareDMembers( this, rightAsD );
      }

    }

    //Test:
    B baseObject = new B();
    D derivedObject = new D();

    // Comparison 1.
    if (baseObject.Equals(derivedObject))
      Console.WriteLine( "Equals" );
    else
      Console.WriteLine( "Not Equal" );

    // Comparison 2.
    if (derivedObject.Equals(baseObject))
      Console.WriteLine( "Equals" );
    else
      Console.WriteLine( "Not Equal" );
    留意一下加粗的部分就知道,如果B的CompareBMembers和D的CompareDMembers比较的元素相等的话,Comparison 1将输出Equals,而Comparison 2将永远输出Not Equal.所以在这种情况下将违反相等语意中的对称性.

    所以还是老老实实在第三步Check类型哈.

    心明眼亮的人肯定主意到上面例子中D比B多了一句:

    if (base.Equals( rightAsD ) == false)
          return false;
    很明显,基类的元素肯定要让基类去判断是否相等.但是如果某个类的直接基类是object千万必要调用base.Equals了,要不然跟没有重写实例Equals是一样的效果,为什么?自己去想吧.

  • 相关阅读:
    SpringMVC在使用Jackson2时关于日期类型格式化的问题
    Redis入门到高可用(八)——list
    LongAdder,AtomicIntegerFieldUpdater深入研究
    Redis入门到高可用(七)——Hash
    CAS缺点
    MySQL 当记录不存在时插入(insert if not exists)
    Redis入门到高可用(六)—— 字符串
    jsonp 跨域
    jvm
    指令重排序
  • 原文地址:https://www.cnblogs.com/Farseer1215/p/331933.html
Copyright © 2020-2023  润新知