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)?
In other words, is this:
if (Equals(item, null)) { /* Do Something */ }
more robust than this:
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?
In reference to "...writing code that will handle objects outside the author's control...", I would point out that both static
Object.Equals
and the==
operator are static methods and therefore cannot be virtual/overridden. Which implementation gets called is determined at compile time based on the static type(s). In other words, there is no way for an external library to provide a different version of the routine to your compiled code.The framework guidelines suggest that you treat
Equals
as value equality (checking to see whether two objects represent the same information, i.e. comparing properties), and==
as reference equality, with the exception of immutable objects, for which you should probably override==
to be value equality.So, assuming the guidelines apply here, pick whichever is semantically sensible. If you're dealing with immutable objects, and you expect both methods to produce identical results, I'd use
==
for clarity.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.
There are actually several different methods you can call to compare object instances. Given two object instances
a
andb
, you could write:Object.Equals(a,b)
Object.ReferenceEquals(a,b)
a.Equals(b)
a == b
These could all do different things!
Object.Equals(a,b)
will (by default) perform reference equality comparison on reference types and bitwise comparison on value types. From the MSDN documentation:Note the last paragraph above ... we'll discuss this a bit later.
Object.ReferenceEquals(a,b)
performs reference equality comparison only. If the types passed are boxed value types, the result is alwaysfalse
.a.Equals(b)
calls the virtual instance method ofObject
, which the type ofa
could override to do anything it wants. The call is performed using virtual dispatch, so the code that runs depends on the runtime type ofa
.a == b
invokes the static overloaded operator of the **compile-time type* ofa
. If the implementation of that operator invokes instance methods on eithera
orb
, 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:So, yes, there is vulnerability for check for nulls using
operator ==
. In practice, most types do not overload==
- but there's never a guarantee.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 theEquals()
member method, in which case this implementation will be called. A user supplied implementation could return whatever it wants, even when comparing to null.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 ofObject.Equals(a,b)
expands to something along the lines of:You can try this for yourself:
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 arenull
. Note thatObject.Equals(a,b)
does not call the instance version ofEquals()
when either of the arguments is null.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 wantObject.ReferenceEquals()
: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:"Hey, this is awkward, let's replace it with:
a.Equals(null)
ora == null
which potentially may be different.
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 likeString
andNullable<T>
have well defined semantics for comparison. Furthermore, they aresealed
- preventing any change to their behavior through inheritance. The following is quite common (and correct):It's unnecessary (and ugly) to write:
So in certain limited cases, using
==
is safe, and appropriate.if (Equals(item, null))
is no more robust thanif (item == null)
, and I find it more confusing to boot.When you want to test IDENTITY (same location in memory):
Handles nulls. And is not overridable. 100% safe.
But make sure you really do want IDENTITY test. Consider the following:
which returns
false
. In contrast:and
both return
true
.If you are expecting an answer of
true
in this situation, then you want an EQUALITY test, not an IDENTITY test. See the next part.When you want to test EQUALITY (same contents):
Use "
a == b
" if the compiler doesn't complain.If that is rejected (if the type of variable a does not define "==" operator), then use "
Object.Equals(a, b)
".IF you are inside of logic where a is known to not be null, THEN you may use the more readable "
a.Equals(b)
". For example, "this.Equals(b)" is safe. Or if "a" is a field that is initialized at construction time, and the constructor throws exception if null is passed in as the value to be used in that field.NOW, to address the original question:
A: Yes. The only way to get 100% safe EQUALITY test would be to pre-test for nulls yourself.
But should you? The bug would be in that (hypothetical future bad class), and it would be a straightforward type of failure. Easy to debug and fix (by whoever supplies the class). I doubt it is a problem that happens often, or persists long when it does happen.
More detailed A:
Object.Equals(a, b)
is most likely to work in the face of a poorly written class. If "a" is null, the Object class will handle it itself, so no risk there. If "b" is null, then the DYNAMIC (run-time not compile-time) type of "a" determines what "Equals" method gets called. The called method merely has to work correctly when "b" is null. Unless the called method is extremely poorly written, the first step it does is determine whether "b" is a type that it understands.So
Object.Equals(a, b)
is a reasonable compromise between readability/coding_effort and safety.