重写 == 运算符。如何与 null 进行比较?

发布于 2024-10-03 17:57:23 字数 1693 浏览 9 评论 0原文

可能的重复:
如何检查空值在没有无限递归的“==”运算符重载中?

可能有一个简单的答案......但它似乎在逃避我。这是一个简化的示例:

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);
}

很好,花花公子,对吧?

但是,当 Person 对象为 null 时会发生什么?

您不能编写:

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

因为这将导致 == 运算符重写运行,并且代码将在 :

person.Equals()

方法调用上失败,因为您无法在 null 实例上调用方法。

另一方面,您无法在 == 覆盖中显式检查此条件,因为它会导致无限递归(以及堆栈溢出 [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);
}

那么,如何覆盖 == 和 != 运算符为了值相等并且仍然考虑空对象?

我希望答案不会简单得令人痛苦。 :-)

Possible Duplicate:
How do I check for nulls in an ‘==’ operator overload without infinite recursion?

There is probably an easy answer to this...but it seems to be eluding me. Here is a simplified example:

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

Let's say that for this particular application, it is valid to say that if the social security numbers match, and both names match, then we are referring to the same "person".

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

To keep things consistent, we override the == and != operators, too, for the developers on the team who don't use the .Equals method.

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

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

Fine and dandy, right?

However, what happens when a Person object is null?

You can't write:

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

Since this will cause the == operator override to run, and the code will fail on the:

person.Equals()

method call, since you can't call a method on a null instance.

On the other hand, you can't explicitly check for this condition inside the == override, since it would cause an infinite recursion (and a Stack Overflow [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);
}

So, how do you override the == and != operators for value equality and still account for null objects?

I hope that the answer is not painfully simple. :-)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

南街女流氓 2024-10-10 17:57:23

使用 object.ReferenceEquals(person1, null) 或新的 is 运算符 而不是 == 运算符:

public static bool operator ==(Person person1, Person person2)
{
    if (person1 is null)
    {
         return person2 is null;
    }

    return person1.Equals(person2);
}

Use object.ReferenceEquals(person1, null) or the new is operator instead of the == operator:

public static bool operator ==(Person person1, Person person2)
{
    if (person1 is null)
    {
         return person2 is null;
    }

    return person1.Equals(person2);
}
吻泪 2024-10-10 17:57:23

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

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 建议的实现 在这里

I've always done it this way (for the == and != operators) and I reuse this code for every object I create:

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);
}

"!=" then goes like this:

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

Edit
I modified the == operator function to match Microsoft's suggested implementation here.

讽刺将军 2024-10-10 17:57:23

你总是可以覆盖并把

(Object)(person1)==null

我想象这会起作用,但不确定。

you could alway override and put

(Object)(person1)==null

I'd imagine this would work, not sure though.

不交电费瞎发啥光 2024-10-10 17:57:23

比这些方法中的任何一种都更容易使用

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

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

Easier than any of those approaches would be to just use

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

This has the same null equality semantics as the approaches that everyone else is proposing, but it's the framework's problem to figure out the details :)

沧笙踏歌 2024-10-10 17:57:23

最终(假设)例程如下。它与@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 执行 null 检查

The final (hypothetical) routine is below. It is very similar to @cdhowie's first accepted response.

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);
}

Thanks for the great responses!

//* - .Equals() performs the null check on person2

小帐篷 2024-10-10 17:57:23

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);
}

Cast the Person instance to 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);
}
ぇ气 2024-10-10 17:57:23

将 Person 转换为 Object,然后执行比较:

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.

Cast the Person to an Object and then perform the comparison:

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.
吹梦到西洲 2024-10-10 17:57:23

cdhowie 通过使用 ReferenceEquals 实现了盈利,但值得注意的是,如果有人直接将 null 传递给 Equals,您仍然可能会遇到异常。另外,如果您要覆盖 Equals ,那么几乎总是值得实现 IEquatable 所以我会这样做。

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));
  }
}

当然,您永远不应该覆盖 Equals 也不应该覆盖 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
      );
  }

Just how much ReferenceEquals(this, other) 的好处捷径可能会根据类的性质而有很大差异,但是否值得这样做是人们应该始终考虑的事情,所以我包括这里的技术。

cdhowie is on the money with the use of ReferenceEquals, but it's worth noting that you can still get an exception if someone passes null directly to Equals. Also, if you are going to override Equals it's almost always worth implementing IEquatable<T> so I would instead have.

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));
  }
}

And of course, you should never override Equals and not override 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();
}

It's also worth noting that identity entails equality (that is, for any valid concept of "equality" something is always equal to itself). Since equality tests can be expensive and occur in loops, and since comparing something with itself tends to be quite common in real code (esp. if objects are passed around in several places), it can be worth adding as a shortcut:

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

Just how much of a benefit short-cutting on ReferenceEquals(this, other) is can vary considerably depending on the nature of the class, but whether it is worth while doing or not is something one should always consider, so I include the technique here.

夏天碎花小短裙 2024-10-10 17:57:23

持续重载这些运算符非常困难。 我对相关问题可以作为模板。

基本上,您首先需要进行引用 (object.ReferenceEquals) 测试以查看对象是否为 null然后你调用Equals

Overloading these operators consistently is pretty hard. My answer to a related question may serve as a template.

Basically, you first need to do a reference (object.ReferenceEquals) test to see if the object is null. Then you call Equals.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文