覆盖 == 运算符。如何与空值进行比较? [复制]
Posted
技术标签:
【中文标题】覆盖 == 运算符。如何与空值进行比较? [复制]【英文标题】:Overriding == operator. How to compare to null? [duplicate] 【发布时间】:2011-05-12 06:43:59 【问题描述】:可能重复: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);
很好,花花公子,对吧?
但是,当 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);
那么,如何覆盖 == 和 != 运算符以实现值相等并仍然考虑空对象?
我希望答案不是简单得令人痛苦。 :-)
【问题讨论】:
SO 正在阻止对这个问题的进一步回答,但从 C# 7 开始,最好的解决方案是使用新的is null
构造,即 if (person1 is null) ...
@Ross,无法添加答案,因为这是一个重复的问题。此外,如果您想指出原始问题的 is null
构造,您会发现其他人在您发表评论前 2 年已经回答了这个问题。
@gog 可能是这样,但这个问题在 Google 搜索中排名第一。似乎......令人难以置信的误导......让这个页面充满(现在)错误的答案没有得到纠正,希望人们会点击重复的链接,而不是使用这个页面上高度赞成和可行但过时的答案之一.如果 SO 的目标是提供帮助,则应尽一切努力完全删除此页面,或使其尽可能好。
【参考方案1】:
使用object.ReferenceEquals(person1, null)
或新的is operator 代替==
运算符:
public static bool operator ==(Person person1, Person person2)
if (person1 is null)
return person2 is null;
return person1.Equals(person2);
【讨论】:
ReferenceEquals
会发出一个方法调用,而强制转换为对象将导致编译器仅发出指令来比较引用是否相等。 ...但这可能算作邪恶的微优化。
@dtb:JIT 无论如何都会内联这个调用;运行时性能将相同。见***.com/questions/735554/…
也许这很明显,但您可能还想对 Person.Equals
覆盖内的 Obj
参数进行空检查。在我看来,person == null
仍然会导致调用 person.Equals(null)
是的,我依靠Equals
方法来检查空参数。我总是编写自己的 Equals
方法来考虑 null 情况,我希望其他人也这样做。 ;)
VS 2017 让我现在使用 is 运算符:left is null && right is null。【参考方案2】:
我一直都是这样做的(对于 == 和 != 运算符),我为我创建的每个对象重复使用此代码:
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);
编辑
我修改了==
运算符函数以匹配微软建议的实现here。
【讨论】:
呃。很抱歉这么说,但这是代码膨胀的一个可怕例子。您需要 14 行来表达可以在带有条件表达式的单个return
语句中表达的内容,就像可读性一样(为了便于阅读,分布在两到三行)。
@Konrad Rudolph - 那么,我该如何改进呢?你想到的两三行是什么?
好吧,一方面,null
检查rhs
是多余的,因为Equals
也必须执行它。其余部分可以表示为return object.ReferenceEquals(lhs, rhs) || !object.ReferenceEquals(lhs, null) && lhs.Equals(rhs);
——显然,使用适当的格式(换行符、缩进)使其可读。如果这太晦涩难懂,请在评论中添加相应 MSDN 文章的链接,该文章描述了相等运算符的语义要求。
我对此表示赞成,因为它完全遵循了微软的建议,但也同意它可以在不失去可读性的情况下进行简化。示例:从 if (System.Object.ReferenceEquals(rhs, null)) 返回 false;可以替换为 return System.Object.ReferenceEquals(rhs, null);【参考方案3】:
你总是可以覆盖并放置
(Object)(person1)==null
我想这会起作用,但不确定。
【讨论】:
它有效……但它有点晦涩难懂(与使用object.ReferenceEquals
相比)。
这实际上非常有效,因为它相当于发出一条 CEQ 指令。 ReferenceEquals 方法调用了一个效率低得多的函数。
@W.KevinHazzard - 这不是真的;对 ReferenceEquals 的调用被优化为指针比较。
你们俩讨论此优化所花费的时间比使用它所期望的要多。【参考方案4】:
比任何这些方法都更容易使用
public static bool operator ==(Person person1, Person person2)
EqualityComparer<Person>.Default.Equals(person1, person2)
这与其他人提出的方法具有相同的空相等语义,但找出细节是框架的问题:)
【讨论】:
【参考方案5】:最终的(假设的)例程如下。这与@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 执行空检查
【讨论】:
【参考方案6】:将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);
【讨论】:
【参考方案7】:将 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.
【讨论】:
【参考方案8】:始终重载这些运算符非常困难。 My answer to a related question 可以作为模板。
基本上,你首先需要做一个引用(object.ReferenceEquals
)测试,看看对象是不是null
。 然后你打电话给Equals
。
【讨论】:
【参考方案9】: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)
上的捷径有多少好处可能会因课程的性质而有很大差异,但是否值得这样做是人们应该始终考虑的事情,所以我在这里包含了这项技术.
【讨论】:
以上是关于覆盖 == 运算符。如何与空值进行比较? [复制]的主要内容,如果未能解决你的问题,请参考以下文章