Is there a difference between x is null and ReferenceEquals(x, null)?

I noticed a lot of answers specifying that x == null, x is null, and ReferenceEquals(x, null) are all equivalent - and for most cases this is true. However, there is a case where you CANNOT use x == null as I have documented below:

Note that the code below assumes you have implemented the Equals method for your class:

Do NOT do this - the operator == method will be called recursively until a stack overflow occurs:

public static bool operator ==(MyClass x1, MyClass x2)
{
   if (x1 == null)
      return x2 == null;

   return x1.Equals(x2)
}

Do this instead:

public static bool operator ==(MyClass x1, MyClass x2)
{
   if (x1 is null)
      return x2 is null;

   return x1.Equals(x2)
}

Or

public static bool operator ==(MyClass x1, MyClass x2)
{
   if (ReferenceEquals(x1, null))
      return ReferenceEquals(x2, null);

   return x1.Equals(x2)
}

Are those really the same?

Semantically yes (assuming x is not a value type). You're doing a null check which is the same for all reference types.

Implementation: no. x == null or x is null will be directly implemented as IL instructions but Object.ReferenceEquals(x, null) will be a method call.1

Also note if the type of x has overridden operator == then x == null may not be equivalent (changing the semantics of null checks in an operator overload is, at best, poor code because no one expects such a semantic change).


1 Of course the optimiser could recognise this and just output the IL, you'll need to look at the IL to confirm this.


I realize that I'm supre-late to the party and that answers have been given, but I feel the need to summarize a bit since this is a thing I search for every 8-12 months or so and I'd like to have an explanation I can understand (hopefully, if it gets posted)..

1. ReferenceEquals(a,b)

This is the tried and tested method to perform a safe reference equality comparison. It basically performs (object)a == (object)b (or something to that effect) and has the advantages that its use is instantly recognizable and it can't be overridden.


2. a == b

This method is the one that feels "natural" to most people (since most comparison done throughout C# will be done with this operator).

Default behaviour on reference types should be correct. However, this can be overloaded, which can lead to unexpected results (imagining a failed implementation of the operator overload).

Like @mdebeus said, an additional risk (however marginal even for a competent monkey that read a primer on C#) is causing a StackOverflowException. This can appear when overloading == and != and using the operators inside the method itself.


3. a is b

OK so this is shiny new sort of sugary thing that we get. Microsoft describes is in this case with:

The is operator checks if the runtime type of an expression result is compatible with a given type.

[...]

The E is T expression returns true if the result of E is non-null and can be converted to type T by a reference conversion, a boxing conversion, or an unboxing conversion; otherwise, it returns false. The is operator doesn't consider user-defined conversions.

(read a complete description here)

The short of it is that this will return true if a can be converted through b via boxing, unboxing or covariance. As you would expect, this works very well against null.


All in all, as a personal note, although is makes things shorter and prettier for null check in equality overloading, I think I'll would still use ReferenceEquals, simply because I'm a control-freak and there is at least a part of how is works that worries me when it comes to cases of covariance.

Tags:

C#