简体   繁体   中英

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM