Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is pattern matching preferred over reference or value equality?

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.

like image 717
Cheesebaron Avatar asked Apr 25 '17 07:04

Cheesebaron


2 Answers

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.

like image 90
David Arno Avatar answered Oct 28 '22 12:10

David Arno


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.

like image 24
GrzegorzF Avatar answered Oct 28 '22 12:10

GrzegorzF