Equals(item,null)或item == null
使用静态Object.Equals检查null的代码是否比使用==运算符或常规Object.Equals的代码更健壮? 是不是后两者容易被覆盖的方式检查null不能按预期工作(例如,当比较值为空时返回false)?
换句话说,是这样的:
if (Equals(item, null)) { /* Do Something */ }
比这更强大:
if (item == null) { /* Do Something */ }
我个人发现后者的语法更易于阅读。 在编写处理作者控制范围之外的对象的代码时(例如库)是否应该避免? 是否应该避免(检查为空时)? 这只是头发分裂?
这个问题没有简单的答案。 在我看来,任何说使用一种或另一种的人都会给你提供可怜的建议。
实际上有几种不同的方法可以调用来比较对象实例。 给定两个对象实例a
和b
,你可以这样写:
Object.Equals(a,b)
Object.ReferenceEquals(a,b)
a.Equals(b)
a == b
这些都可以做不同的事情!
Object.Equals(a,b)
将(默认情况下)对引用类型执行引用相等比较,并对值类型进行按位比较。 从MSDN文档:
Equals的默认实现支持引用类型的引用相等,以及值类型的按位相等。 引用相等意味着被比较的对象引用指的是同一个对象。 按位相等意味着被比较的对象具有相同的二进制表示。
请注意,派生类型可能会覆盖Equals方法以实现值相等。 值相等意味着比较对象具有相同的值但不同的二进制表示。
请注意上面的最后一段......我们稍后再讨论这个问题。
Object.ReferenceEquals(a,b)
仅执行参考相等比较。 如果传递的类型是装箱值类型,则结果始终为false
。
a.Equals(b)
调用的虚拟实例方法Object
,其类型a
可以覆盖做任何事情就是了。 该调用是使用虚拟调度执行的,因此运行的代码取决于a
的运行时类型。
a == b
调用的**编译时类型*的静态重载操作a
。 如果该运算符的实现在a
或b
上调用实例方法,则它也可能取决于参数的运行时类型。 由于分派基于表达式中的类型,因此以下内容可能会产生不同的结果:
Frog aFrog = new Frog();
Frog bFrog = new Frog();
Animal aAnimal = aFrog;
Animal bAnimal = bFrog;
// not necessarily equal...
bool areEqualFrogs = aFrog == bFrog;
bool areEqualAnimals = aAnimal = bAnimal;
所以,是的,使用operator ==
检查空值时存在漏洞。 实际上,大多数类型不会重载==
- 但是从来没有保证。
实例方法Equals()
在这里没有更好的。 虽然默认实现执行引用/逐位相等检查,但类型可能会覆盖Equals()
成员方法,在这种情况下,将会调用此实现。 用户提供的实现可以返回任何想要的结果,即使与null进行比较。
但是,你问的Object.Equals()
的静态版本呢? 这可以最终运行用户代码? 那么,事实证明答案是肯定的。 Object.Equals(a,b)
扩展到如下Object.Equals(a,b)
行:
((object)a == (object)b) || (a != null && b != null && a.Equals(b))
你可以自己尝试一下:
class Foo {
public override bool Equals(object obj) { return true; } }
var a = new Foo();
var b = new Foo();
Console.WriteLine( Object.Equals(a,b) ); // outputs "True!"
因此,声明中有可能: Object.Equals(a,b)
在调用中的任何类型都为null
时运行用户代码。 请注意,当任一参数为空时, Object.Equals(a,b)
不会调用Equals()
的实例版本。
总之,您获得的比较行为的种类可能会有很大差异,具体取决于您选择调用哪种方法。 然而,这里有一个评论:微软并没有正式记录Object.Equals(a,b)
的内部行为。 如果你需要一个熨斗gaurantee比较引用null而没有任何其他代码运行,你想Object.ReferenceEquals()
:
Object.ReferenceEquals(item, null);
这种方法使意图得到极端清晰 - 特别期望结果是两个引用相等的参考比较。 使用诸如Object.Equals(a,null)
类的东西的好处在于,稍后某人稍后再来并说:
“嘿,这很尴尬,让我们用: a.Equals(null)
或者a == null
替换它
这可能会有所不同。
但是,让我们在这里注入一些实用主义。 到目前为止,我们已经谈到了不同的比较方式的潜力,以产生不同的结果。 虽然情况确实如此,但某些类型可以安全地编写a == null
。 像String
和Nullable<T>
这样的内置.NET类具有定义好的用于比较的语义。 此外,他们是sealed
- 防止通过继承来改变他们的行为。 以下是很常见的(并且是正确的):
string s = ...
if( s == null ) { ... }
这是不必要的(也很难看):
if( ReferenceEquals(s,null) ) { ... }
所以在某些有限的情况下,使用==
是安全的,并且是适当的。
if (Equals(item, null))
不比if (item == null)
更强健,并且我觉得它更难以启动。
框架指南建议您将Equals
视为值相等(检查两个对象是否代表相同的信息,即比较属性),并将==
作为引用相等,除了不可变对象之外,您应该重写==
价值平等。
因此,假设这些准则适用于此,请选择语义上合理的语言。 如果你正在处理不可变的对象,并且你希望这两种方法产生相同的结果,为了清晰起见,我会使用==
。