代码之家  ›  专栏  ›  技术社区  ›  Flipster

重写==运算符。如何与空值进行比较?[副本]

  •  120
  • Flipster  · 技术社区  · 14 年前

    可能重复:
    How do I check for nulls in an ‘==’ operator overload without infinite recursion?

    也许有一个简单的答案…但它似乎是逃避我。下面是一个简单的例子:

    public class Person
    {
       public string SocialSecurityNumber;
       public string FirstName;
       public string LastName;
    }
    

    假设对于这个特定的应用程序,可以说如果社保号码匹配,并且两个名字都匹配,那么我们指的是同一个“人”。

    public override bool Equals(object Obj)
    {
        Person other = (Person)Obj;
        return (this.SocialSecurityNumber == other.SocialSecurityNumber &&
            this.FirstName == other.FirstName &&
            this.LastName == other.LastName);
    }
    

    为了保持一致,我们重写了==和!=对于团队中不使用 .Equals 方法。

    public static bool operator !=(Person person1, Person person2)
    {
        return ! person1.Equals(person2);
    }
    
    public static bool operator ==(Person person1, Person person2)
    {
        return person1.Equals(person2);
    }
    

    又好又漂亮,对吧?

    但是,当一个人对象是 null ?

    你不能写:

    if (person == null)
    {
        //fail!
    }
    

    因为这将导致==运算符重写运行,并且代码将在以下位置失败:

    person.Equals()
    

    方法调用,因为不能对空实例调用方法。

    另一方面,不能在==重写中显式检查此条件,因为它会导致无限递归(以及堆栈溢出[dot com])

    public static bool operator ==(Person person1, Person person2)
    {
        if (person1 == null)
        {
             //any code here never gets executed!  We first die a slow painful death.
        }
        return person1.Equals(person2);
    }
    

    那么,如何重写==和!=值相等的运算符,但仍占空对象?

    我希望答案不那么简单。:-)

    9 回复  |  直到 7 年前
        1
  •  241
  •   Mark Brackett Achilles Ram Nakirekanti    6 年前

    使用 object.ReferenceEquals(person1, null) 而不是 == 操作员:

    public static bool operator ==(Person person1, Person person2)
    {
        if (object.ReferenceEquals(person1, null))
        {
             return object.ReferenceEquals(person2, null);
        }
    
        return person1.Equals(person2);
    }
    
        2
  •  17
  •   Mike Webb    11 年前

    我总是这样做的(为了==和!=运算符)并且我为创建的每个对象重用此代码:

    public static bool operator ==(Person lhs, Person rhs)
    {
        // If left hand side is null...
        if (System.Object.ReferenceEquals(lhs, null))
        {
            // ...and right hand side is null...
            if (System.Object.ReferenceEquals(rhs, null))
            {
                //...both are null and are Equal.
                return true;
            }
    
            // ...right hand side is not null, therefore not Equal.
            return false;
        }
    
        // Return true if the fields match:
        return lhs.Equals(rhs);
    }
    

    "!=”然后这样说:

    public static bool operator !=(Person lhs, Person rhs)
    {
        return !(lhs == rhs);
    }
    

    编辑
    我修改了 == 与Microsoft建议的实现相匹配的运算符函数 here .

        3
  •  11
  •   rtpg    14 年前

    你可以随时超越

    (Object)(person1)==null
    

    我想这是可行的,但不确定。

        4
  •  3
  •   Flipster    14 年前

    最后(假设)的程序如下。这与@cdhowie第一次接受的回答非常相似。

    public static bool operator ==(Person person1, Person person2)
    {
        if (Person.ReferenceEquals(person1, person2)) return true;
        if (Person.ReferenceEquals(person1, null)) return false; //*
        return person1.Equals(person2);
    }
    

    谢谢你的回复!

    //* - .Equals() 对person2执行空检查

        5
  •  2
  •   dtb    14 年前

    投下 Person 实例到 object :

    public static bool operator ==(Person person1, Person person2)
    {
        if ((object)person1 == (object)person2) return true;
        if ((object)person1 == null) return false;
        if ((object)person2 == null) return false;
        return person1.Equals(person2);
    }
    
        6
  •  2
  •   Greg Sansom    14 年前

    将此人投射到对象上,然后执行比较:

    object o1 = (object)person1;
    object o2 = (object)person2;
    if(o1==o2) //compare instances.
       return true;
    if (o1 == null || o2 == null)  //compare to null.
       return false;
    //continue with Person logic.
    
        7
  •  2
  •   start    14 年前

    比任何一种方法都简单

    public static bool operator ==(Person person1, Person person2)   
    {   
        EqualityComparer<Person>.Default.Equals(person1, person2)
    } 
    

    这与其他人提出的方法具有相同的空等式语义,但要找出细节是框架的问题:)

        8
  •  1
  •   Community M-A    7 年前

    始终重载这些运算符是相当困难的。 My answer to a related question 可以用作模板。

    基本上,你首先需要做一个参考( object.ReferenceEquals )测试对象是否 null . 那么 你打电话来 Equals .

        9
  •  1
  •   Jon Hanna    14 年前

    cdhowie正在使用 ReferenceEquals ,但值得注意的是,如果有人通过,仍然可以获得异常 null 直接到 Equals . 另外,如果你要重写 等于 几乎总是值得实施的 IEquatable<T> 所以我宁愿。

    public class Person : IEquatable<Person>
    {
      /* more stuff elided */
    
      public bool Equals(Person other)
      {
        return !ReferenceEquals(other, null) &&
          SocialSecurityNumber == other.SocialSecurityNumber &&
          FirstName == other.FirstName &&
          LastName == other.LastName;
      }
      public override bool Equals(object obj)
      {
        return Equals(obj as Person);
      }
      public static bool operator !=(Person person1, Person person2)
      {
        return !(person1 == person2);
      }
      public static bool operator ==(Person person1, Person person2)
      {
        return ReferenceEquals(person1, person2)
          || (!ReferenceEquals(person1, null) && person1.Equals(person2));
      }
    }
    

    当然,你不应该无视 等于 而不是重写 GetHashCode()

    public override int GetHashCode()
    {
       //I'm going to assume that different
       //people with the same SocialSecurityNumber are extremely rare,
       //as optimise by hashing on that alone. If this isn't the case, change this
       return SocialSecurityNumber.GetHashCode();
    }
    

    同样值得注意的是,身份意味着平等(也就是说,对于任何有效的“平等”概念,有些东西总是等于它自己)。由于相等性测试可能很昂贵,并且会在循环中发生,而且由于在实际代码中比较某个对象本身往往非常常见(特别是如果对象在多个地方传递),因此值得添加一个快捷方式:

      public bool Equals(Person other)
      {
        return !ReferenceEquals(other, null) &&
          ReferenceEquals(this, other) ||
          (
            SocialSecurityNumber == other.SocialSecurityNumber &&
            FirstName == other.FirstName &&
            LastName == other.LastName
          );
      }
    

    有多少好处 ReferenceEquals(this, other) is可以根据类的性质有很大的不同,但是它是否值得做是一个应该经常考虑的问题,所以我在这里包括了技巧。