Why do we need it?
Remember boring polymorphism examples where Circle
and Square are derived from Shape and
each one override Draw method? This works well if
you are dealing with classes. What if you just have simple values
and no inheritance is possible?
New C#7 extended existing is and switch
language constructs to check if item is of the certain "shape". Now
you can execute different code according to "shape" of the tested
item (similar to polymorphism). In the past is was
testing only against a type and case was matching
only constant values. Today both can test against pattern containing
value types as well as reference types.
is example
If pattern is matched value can be extracted to a variable.
This is the part I love. Example below tests each list
item with is keyword against int or IEnumerable.
If match is confirmed item is extracted to a new
variable (value or list). As a result
we will add up all numbers from complex list containing not only
integers but lists too.
We can write something similar without pattern matching but code above is more concise and more readable.
Prior to the version 7 is expression was testing only against type. Now we can test against constant or even structure. On the right side of is now we can have a pattern (will introduce more complex patterns later). Same goes for case inside of switch.
switch example
For the next example lets switch to switch (pun intended). Same as is, switch now supports pattern matching. Let's improve our code and test against constant 0, null value and also introduce default case. Note that default case will be executed last regardless of where is located in the switch statement. To avoid confusion, put it at the end. All other cases are executed in textual order.
We are switching on any type and not only primitive types as before.
Breaking changes
- You can't fall thru cases! Each case must end with
break,returnorgoto. - Order is important!
case 0must be beforecase intor it will never be matched (zero case is subset of int case).
Good news is that compiler will generate error for any of above.
when condition
Pattern can be enriched with conditions. Let's introduce matching to a structure (value type) and also apply when clause. Our funny list now contains structure Person. Person age will be included in sum only if person is 18 or older. Also we will introduce nullList to show how case IEnumerable<object> list will not match this null.
Again, order is important! case Person p when p.Age < 18 must be before case Person p since it is more restrictive! In example above we merged two cases (zero and person < 18) but we are not using any variable inside that block. We are just doing break. Keep in mind that using p inside first block would be illegal since when case 0 is executed p is unassigned! No worries, compiler will detect that.
case null
Matching pattern guarantees a non-null value. In example above, compiler didn't complain that case null is unreachable. All cases are checked against null and they will not match if variable is null. Therefore we need to handle null separately.
var pattern
var pattern will match everything including null ! Proof that it will match everything is a compiler error if you uncomment case int or case null in example below. Since matching with var is before those two cases they are unreachable.
A million dollar question is why compiler didn't complain about default case?
Inconsistent scope
Defining variables in if-else has a bit of inconsistency.
Variable text is defined in if statement and has scope same as if statement itself - entire surrounding function.
If we try to use it after if-else statement compiler will complain that variable is unassigned. On the other side if we try to use number
variable from else statement in the same way error will be different. Variable is out of scope. If we understand that else-if is
actually another if inside else block all makes sense.
Conclusion
Pattern matching is one more step forward in making C# more concise and readable. At first having four items in if
expression was strange. Now looks so normal.