I see a lot of examples on how to use pattern matching in C#7. It all looks good. However, I have a question, which I do not seem to be able to find an answer for.
Say you have the following expression:
if (a is null)
My question is: Is it preferred to use pattern matching instead of reference or value equality with C#7?
So instead of writing:
if (a == null)
or:
if (a.Equals(null))
or:
if (object.Equals(a, null))
I suspect the a is null
generates something like the last expression. But would it in general be preferred to switch to pattern matching?
Correct me if I am wrong and this is a primarily opinion based question, but I could not seem to find a definitive answer that backed this up.
Consider the following four code snippets:
// 1
var x = "";
var y = x is null;
// 2
var x = "";
var y = x.Equals(null);
// 3
var x = "";
var y = object.Equals(x, null);
// 4
var x = "";
var y = x == null;
The IL for these, respectively, is:
// 1
IL_0001: ldstr ""
IL_0006: stloc.0
IL_0007: ldnull
IL_0008: ldloc.0
IL_0009: call bool [mscorlib]System.Object::Equals(object, object)
IL_000e: stloc.1
// 2
IL_0001: ldstr ""
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldnull
IL_0009: call bool [mscorlib]System.Object::Equals(object, object)
IL_000e: stloc.1
// 3
IL_0001: ldstr ""
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldnull
IL_0009: call bool [mscorlib]System.Object::Equals(object, object)
IL_000e: stloc.1
// 4
IL_0001: ldstr ""
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldnull
IL_0009: ceq
IL_000b: stloc.1
As you can see, the first three result in near identical code. The ==
version uses ceq
rather than .Equals()
.
I'm guessing ceq
is faster, and thus x == null
is the fastest way of testing for null
. Beyond that, it becomes a matter of preferred style.
No matter if optimization is enabled, IL:
r = a is null;
IL_0004: ceq
r = a.Equals(null);
IL_0009: callvirt instance bool [mscorlib]System.Object::Equals(object)
r = Equals(a, null);
IL_0011: call bool [mscorlib]System.Object::Equals(object, object)
r = a == null;
IL_0019: ceq
r = a == default;
IL_001e: ceq
r = a == default(object);
IL_0023: ceq
r = ReferenceEquals(a, null);
IL_0028: ceq
I removed the repetitive noise for each expression:
IL_0002: ldloc.0
IL_0003: ldnull
// ...
IL_0006: stloc.1
a.Equals(null)
will throw an exception if a
= null
, so it is far from preferred.
Most probably a == null
is used (short form, desired effect).
It should be noted that the operator ==
and method object.Equals
can be overridden.
In the case that certain class have bad impacts with (significant performance degradation or worse - throws exception on) a == null
then you can use for sure ReferenceEquals(a, null)
.
ReferenceEquals
always performs only the comparison of two objects and also uses ceq
.
Lack of call
for ReferenceEquals
means for me something à la AggressiveInlining
(IL size: 5).
Did you can use is
? Of course you can.
But it look strange for me. You do not check the a
type, rather you ask if a
points to null.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With