重写 = = 操作符。如何比较空?

可能的复制品:
如何在没有无限递归的情况下检查‘ = =’运算符重载中的 null?

这个问题可能有一个简单的答案... ... 但我似乎不明白。这里有一个简单的例子:

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()

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

另一方面,不能在 = = 覆盖内显式检查这个条件,因为它会导致无限递归(以及 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);
}

那么,如何在覆盖 = = 和! = 运算符以获得值相等的同时仍然考虑空对象呢?

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

73708 次浏览

使用 object.ReferenceEquals(person1, null)或新的 是接线员代替 ==操作符:

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


return person1.Equals(person2);
}

你可以重写

(Object)(person1)==null

不过我不确定能不能成功。

始终如一地重载这些操作符是相当困难的。 我对一个相关问题的回答可以作为一个模板。

基本上,您首先需要做一个引用(object.ReferenceEquals)测试,以查看对象是否是 null。你调用 Equals

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

将 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.

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

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

剪辑
我修改了 ==操作符函数以匹配微软建议的实现 给你

最后一个(假设的)例程如下所示,它非常类似于@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()对 person 2执行空检查

Cdhowie 使用了 ReferenceEquals,但是值得注意的是,如果有人将 null直接传递给 Equals,您仍然可能会遇到异常。另外,如果你要覆盖 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));
}
}

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

ReferenceEquals(this, other)上抄近路的好处有多少,取决于类的性质,可能会有很大的不同,但是是否值得一边做一边考虑,所以我在这里介绍了这种技术。

仅仅使用这些方法比任何一种方法都要容易

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

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