覆盖==运算符。 如何比较为空?

可能重复:
如何在没有无限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); } 

精致而华丽,对吗?

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

你不能写:

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

由于这将导致==运算符覆盖运行,并且代码将失败:

 person.Equals() 

方法调用,因为你不能在空实例上调用一个方法。

另一方面,你不能明确地检查==覆盖内的这个条件,因为它会导致无限recursion(和堆栈溢出[点击])

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

那么,如何重写==和!=运算符来实现值相等,并仍占空对象呢?

我希望答案不是很简单。 🙂

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

你可以总是覆盖和放

 (Object)(person1)==null 

我想这会工作,但不知道。

我一直这样做(对于==和!=运算符),我重复使用这个代码为我创build的每个对象:

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

编辑
我修改了==运算符函数以匹配Microsoftbuild议的实现。

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

一致地重载这些操作符是非常困难的。 我对相关问题的回答可以作为模板。

基本上,你首先需要做一个引用( object.ReferenceEquals )testing,看看对象是否为null然后你打电话给Equals

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

最后的(假设)例程如下。 这与@ 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检查

cdhowie是使用ReferenceEquals的钱,但值得注意的是,如果有人将null直接传递给Equals ,你仍然可以得到一个exception。 另外,如果你打算重写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(); } 

同样值得注意的是,身份需要平等(也就是说,对于任何有效的“平等”概念而言,某事总是等于自身)。 由于相等性testing可能是昂贵的,并且出现在循环中,并且由于在真实的代码中比较某些东西往往是相当普遍的(特别是如果对象在几个地方传递),所以值得添加一个快捷方式:

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

ReferenceEquals(this, other)快捷方式有多less可以根据类的性质而有很大的不同,但是在做或不做的时候值得一看,所以我把这个技巧包含在这里。

比任何这些方法更容易使用

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

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