是使用 static Object.Equals 来检查null 比使用 == 运算符或 常规 Object.Equals?后两者是不是很容易被覆盖,以至于检查 null 不能按预期工作(例如,当比较值 为 null 时返回 false)?
Is code that uses the static Object.Equals to check for null more robust than code that uses the == operator or regular Object.Equals? Aren't the latter two vulnerable to being overridden in such a way that checking for null doesn't work as expected (e.g. returning false when the compared value is null)?
换句话说,是这样的:
if (Equals(item, null)) { /* Do Something */ }
比这更强大:
if (item == null) { /* Do Something */ }
我个人觉得后一种语法更容易阅读.在编写处理作者控制之外的对象(例如库)的代码时是否应该避免?是否应该始终避免(检查空值时)?这只是头发分裂吗?
I personally find the latter syntax easier to read. Should it be avoided when writing code that will handle objects outside the author's control (e.g. libraries)? Should it always be avoided (when checking for null)? Is this just hair-splitting?
这个问题没有简单的答案.在我看来,任何说总是使用其中一种的人都给了你糟糕的建议.
There's no simple answer for this question. Anyone who says always use one or the other is giving you poor advice, in my opinion.
实际上可以调用几种不同的方法来比较对象实例.给定两个对象实例 a
和 b
,你可以这样写:
There are actually several different methods you can call to compare object instances. Given two object instances a
and b
, you could write:
Object.Equals(a,b)
Object.ReferenceEquals(a,b)
a.Equals(b)
a == b
这些都可以做不同的事情!
Object.Equals(a,b)
将(默认情况下)对引用类型进行引用相等比较,并对值类型进行按位比较.来自 MSDN 文档:
Object.Equals(a,b)
will (by default) perform reference equality comparison on reference types and bitwise comparison on value types. From the MSDN documentation:
Equals 的默认实现支持引用相等引用类型和按位相等对于值类型.引用相等表示对象引用是比较指的是同一个对象.按位相等意味着对象比较具有相同的二进制代表.
The default implementation of Equals supports reference equality for reference types, and bitwise equality for value types. Reference equality means the object references that are compared refer to the same object. Bitwise equality means the objects that are compared have the same binary representation.
请注意,派生类型可能将 Equals 方法覆盖为实现价值平等.价值相等意味着被比较的对象具有相同的值但不同二进制表示.
Note that a derived type might override the Equals method to implement value equality. Value equality means the compared objects have the same value but different binary representations.
注意上面的最后一段......我们稍后会讨论这个.
Note the last paragraph above ... we'll discuss this a bit later.
Object.ReferenceEquals(a,b)
仅执行引用相等性比较.如果传递的类型是装箱值类型,结果总是false
.
a.Equals(b)
调用Object
的虚实例方法,a
的类型可以覆盖做任何它想做的事情.调用是使用虚拟分派执行的,因此运行的代码取决于a
的运行时类型.
a.Equals(b)
calls the virtual instance method of Object
, which the type of a
could override to do anything it wants. The call is performed using virtual dispatch, so the code that runs depends on the runtime type of a
.
a == b
调用a
的**编译时类型* 的静态重载运算符.如果该运算符的实现调用 a
或 b
上的实例方法,则它还可能取决于参数的运行时类型.由于分派基于表达式中的类型,因此以下可能会产生不同的结果:
a == b
invokes the static overloaded operator of the **compile-time type* of a
. If the implementation of that operator invokes instance methods on either a
or b
, it may also depend on the runtime types of the parameters. Since the dispatch is based on the types in the expression, the following may yield different results:
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 ==
检查空值存在漏洞. 实际上,大多数类型不会重载 ==
- 但永远没有保证.
So, yes, there is vulnerability for check for nulls using operator ==
. In practice, most types do not overload ==
- but there's never a guarantee.
实例方法 Equals()
在这里也好不到哪里去.虽然默认实现执行引用/按位相等性检查,但类型可能会覆盖 Equals()
成员方法,在这种情况下将调用此实现.用户提供的实现可以返回它想要的任何东西,即使与 null 相比也是如此.
The instance method Equals()
is no better here. While the default implementation performs reference/bitwise equality checks, it is possible for a type to override the Equals()
member method, in which case this implementation will be called. A user supplied implementation could return whatever it wants, even when comparing to null.
但是你问的 Object.Equals()
的静态版本怎么样?这最终可以运行用户代码吗?好吧,事实证明答案是肯定的.Object.Equals(a,b)
的实现扩展为:
But what about the static version of Object.Equals()
you ask? Can this end up running user code? Well, it turns out that the answer is YES. The implementation of Object.Equals(a,b)
expands to something along the lines of:
((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!"
因此,当调用中的任何一种类型都不是 null
时,语句:Object.Equals(a,b)
可能会运行用户代码.请注意,当任一参数为空时,Object.Equals(a,b)
不会调用 Equals()
的实例版本.
As a consequence, it's possible for the statement: Object.Equals(a,b)
to run user code when neither of the types in the call are null
. Note that Object.Equals(a,b)
does not call the instance version of Equals()
when either of the arguments is null.
简而言之,您获得的比较行为类型可能会有很大差异,具体取决于您选择调用的方法.然而,这里有一条评论:Microsoft 没有正式记录 Object.Equals(a,b)
的内部行为.如果您需要在不运行任何其他代码的情况下将引用与 null 进行比较的铁一般的保证,您需要 Object.ReferenceEquals()
:
In short, the kind of comparison behavior you get can vary significantly, depending on which method you choose to call. One comment here, however: Microsoft doesn't officially document the internal behavior of Object.Equals(a,b)
. If you need an iron clad gaurantee of comparing a reference to null without any other code running, you want Object.ReferenceEquals()
:
Object.ReferenceEquals(item, null);
此方法使意图非常明确 - 您特别期望结果是两个引用的比较以实现引用相等.与使用 Object.Equals(a,null)
之类的东西相比,这里的好处是以后不太可能有人过来说:
This method makes the intent extremently clear - you are specifically expecting the result to be the comparison of two references for reference equality. The benefit here over using something like Object.Equals(a,null)
, is that it's less likely that someone will come along later and say:
嘿,这很尴尬,让我们将其替换为:a.Equals(null)
或 a == null
可能会有所不同.
不过,让我们在这里注入一些实用主义. 到目前为止,我们已经讨论了不同的比较方式产生不同结果的可能性.虽然情况确实如此,但在某些类型中,编写 a == null
是安全的.String
和 Nullable
等内置 .NET 类具有定义明确的语义以进行比较.此外,它们是密封
- 防止通过继承对其行为进行任何更改.以下是很常见的(而且是正确的):
Let's inject some pragmatism here, however. So far we've talked about the potential for different modalities of comparison to yield different results. While this is certainly the case, there are certain types where it's safe to write a == null
. Built-in .NET classes like String
and Nullable<T>
have well defined semantics for comparison. Furthermore, they are sealed
- preventing any change to their behavior through inheritance. The following is quite common (and correct):
string s = ...
if( s == null ) { ... }
写是不必要的(而且很丑):
It's unnecessary (and ugly) to write:
if( ReferenceEquals(s,null) ) { ... }
因此在某些有限的情况下,使用 ==
是安全且适当的.
So in certain limited cases, using ==
is safe, and appropriate.
这篇关于Equals(item, null) 或 item == null的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!