Patterns in switch
In the theme on conditions, the arms of a switch expression compared a value with specific numbers: 1 => "Lobby". For a grade by score, that would take an arm for every number from 0 to 100. But an arm can hold any pattern from the previous lesson.
Back to the sorting machine. Its slots stand in a row, and a parcel rides the belt past them from top to bottom. The parcel drops through the first slot it fits. It never reaches the lower slots, even if it would fit through them too.
A switch expression with patterns works the same way:
Console.WriteLine(Grade(95)); // A
Console.WriteLine(Grade(72)); // C
Console.WriteLine(Grade(-5)); // invalid
string Grade(int score) => score switch
{
< 0 or > 100 => "invalid",
>= 90 => "A",
>= 80 => "B",
>= 70 => "C",
_ => "F",
};Let's walk the call Grade(72) through the arms:
< 0 or > 100doesn't match:72lies between0and100.>= 90doesn't match, and neither does>= 80.>= 70matches, and the whole expression gets the value"C".- The
_arm isn't checked at all: the choice ends at the first matching arm.
The arms are checked in order
You know this rule from the theme on conditions: the arms are checked from top to bottom, and the first matching one wins. With specific numbers, it was hard to notice: a value equals only one of them.
Patterns overlap. The number 95 matches three arms at once: >= 90, >= 80 and >= 70. The first of them wins, so Grade(95) gives A. The order of the arms is part of the program's meaning: put narrow arms above wide ones.
In the widget, the method Kind is called three times, with different numbers. Press "Next step" and watch which arm each of them stops at.
1Console.WriteLine(Kind(5));2Console.WriteLine(Kind(50));3Console.WriteLine(Kind(500));4 5string Kind(int n) => n switch6{7 < 10 => "small",8 < 100 => "medium",9 _ => "large",10};
Main calls Kind(5) and waits for the result to print it.
The number 5 fits both < 10 and < 100, but the check stopped at the first arm. Only 500 got down to _: it matched none of the arms above.
Interviewers like to ask what happens when a value matches several arms. The answer: the topmost one fires, and the rest are not even checked.
The compiler still makes sure every value has an arm, and warns with CS8509 if it doesn't. It works out ranges on its own: n switch { < 0 => "-", >= 0 => "+" } covers every number, and no _ arm is needed here.
Which patterns go into arms
You have already seen relational patterns in Grade. Two more kinds show up in arms all the time.
The type pattern
An arm can check the type and give you a variable at once, like is int n:
Console.WriteLine(Describe(7)); // int 7
Console.WriteLine(Describe("hi")); // text hi
Console.WriteLine(Describe(true)); // other
string Describe(object x) => x switch
{
int n => "int " + n,
string s => "text " + s,
_ => "other",
};The variable n exists only in its own arm, to the right of =>. The value true matched neither int nor string and went to the _ arm.
The property pattern
A property pattern checks a value's properties. The pattern { X: 0 } matches if the property X equals 0:
var p = new Point(0, 5);
Console.WriteLine(p switch
{
{ X: 0, Y: 0 } => "origin",
{ X: 0 } => "on the Y axis",
{ Y: 0 } => "on the X axis",
_ => "elsewhere",
}); // on the Y axis
record Point(int X, int Y);The properties in braces are separated by commas, and all of them must match. Any pattern can stand after the colon, for example { X: > 0 }. For the point (0, 5), the property Y is not 0, so the first arm doesn't match, and the second one does.
Common mistake
A wide arm was put above a narrow one:
int score = 95;
string grade = score switch
{
>= 70 => "C",
>= 90 => "A", // ✗
_ => "F",
};
Console.WriteLine(grade);error CS8510: The pattern is unreachable. It has already been handled by a previous arm of the switch expression or it is impossible to match.The program doesn't compile. Any number from 90 up also matches >= 70, so the arm >= 90 can never win. Put >= 90 above >= 70, as in Grade.
This is where the sorting-machine analogy breaks down. A machine in a warehouse doesn't know in advance whether a parcel will fit a slot. The compiler sees all the arms before the program runs and won't accept an arm that no value can ever reach.
A partial overlap is not an error. In Grade, the number 85 matches both >= 80 and >= 70: the compiler silently accepts this, and at run time the number goes to the first of them. Checking that order is up to you.
Key points
- The arms of a
switchexpression are checked from top to bottom, and the first matching one fires. - Arms hold any patterns: relational
>= 90, typeint n, property{ X: 0 }. - An arm fully covered by the arms above it doesn't compile:
CS8510. - Partially overlapping arms compile, and the value goes to the first of them.