switch Expressions & Pattern Matching
When one value can lead to many different results, a long else if chain gets hard to read. C# offers switch for this, in two forms: the classic switch statement and the modern switch expression. Both are powered by patterns, small descriptions of the shape of a value, such as "greater than 90", "a string" or "a list that ends with 6".
Pattern matching is one of the features that makes modern C# pleasant to write. In this lesson you will start with the classic statement and move step by step to the patterns used in real code today.
The switch statement
A switch statement compares one value against several case labels and runs the matching section. default runs when nothing else matches.
int day = 3;
switch (day)
{
case 1:
Console.WriteLine("Monday: maths test");
break;
case 2:
case 3:
Console.WriteLine("Practical lab");
break;
case 6:
case 7:
Console.WriteLine("Weekend");
break;
default:
Console.WriteLine("Regular classes");
break;
}Practical lab
Each section must end with break (or return or throw). Unlike C and Java, C# never lets one section silently fall through into the next; forgetting break is compile error CS0163. Stacking labels, as with case 2: and case 3:, is how you share one section between several values.
switch expressions
Most of the time you use switch to choose a value. A switch expression (C# 8) does exactly that, with much less noise: the value comes first, then switch, then arms of the form pattern => result, separated by commas.
int marks = 82;
string grade = marks switch
{
>= 90 => "A+",
>= 75 => "A",
>= 60 => "B",
>= 40 => "C",
_ => "Needs improvement"
};
Console.WriteLine($"Grade: {grade}");
int month = 7;
string season = month switch
{
12 or 1 or 2 => "Winter",
>= 3 and <= 5 => "Summer",
>= 6 and <= 9 => "Monsoon",
10 or 11 => "Post-monsoon",
_ => "Invalid month"
};
Console.WriteLine($"Month {month}: {season}");Grade: A Month 7: Monsoon
Several kinds of pattern appear here:
- Relational patterns such as
>= 90compare the value with a constant. - Logical patterns combine patterns with
and,orandnot.>= 6 and <= 9means "from 6 to 9". - Constant patterns such as
12match one exact value. - The discard pattern
_matches anything, likedefault.
Arms are tried from top to bottom and the first match wins, just like else if. A switch expression must produce a value for every input. If no arm matches, the program throws a SwitchExpressionException at run time, and the compiler warns you in advance with CS8509 ("the switch expression does not handle all possible values"). Ending with a _ arm is the usual fix.
Type patterns and when guards
Patterns can also test a value's type and give it a name in the same step. A when clause adds an extra condition to an arm.
object?[] values = [42, -7, "Pune", 7.5, true, null];
foreach (object? v in values)
{
string description = v switch
{
int n when n > 0 => $"positive int {n}",
int n => $"int {n}",
string s => $"text of length {s.Length}",
double d => $"double {d}",
null => "nothing at all",
_ => $"something else: {v}"
};
Console.WriteLine(description);
}positive int 42 int -7 text of length 4 double 7.5 something else: True nothing at all
int n is a type pattern with a declaration: if v holds an int, the arm matches and the number is available as n, already converted. The foreach loop simply visits each item in the array; you will learn it in Loops. The same type test works in an if: if (v is string s && s.Length > 3) { ... }.
Property and tuple patterns
A property pattern looks inside an object and matches its properties with { Name: pattern }. A tuple pattern switches on several values at once by putting them in brackets.
DateTime[] dates =
[
new DateTime(2027, 1, 26),
new DateTime(2026, 8, 15),
new DateTime(2026, 11, 14),
new DateTime(2026, 12, 2)
];
foreach (DateTime d in dates)
{
string kind = d switch
{
{ Month: 1, Day: 26 } => "Republic Day",
{ Month: 8, Day: 15 } => "Independence Day",
{ DayOfWeek: DayOfWeek.Saturday or DayOfWeek.Sunday } => "Weekend",
_ => "Working day"
};
Console.WriteLine($"{d:dd MMM yyyy}: {kind}");
}
string coach = "3A";
bool tatkal = true;
int fare = (coach, tatkal) switch
{
("SL", false) => 450,
("SL", true) => 600,
("3A", false) => 1170,
("3A", true) => 1500,
_ => 0
};
Console.WriteLine($"{coach}, tatkal {tatkal}: Rs {fare}");26 Jan 2027: Republic Day 15 Aug 2026: Independence Day 14 Nov 2026: Weekend 02 Dec 2026: Working day 3A, tatkal True: Rs 1500
15 August 2026 is a Saturday, yet it prints "Independence Day" because that arm comes first. The fares are invented for the example. Tuple patterns are a tidy replacement for nested if statements that check two or three values together.
List patterns
int[][] overs = [[4, 6, 0, 6], [6, 1, 2], [1, 0, 4], []];
foreach (int[] balls in overs)
{
string summary = balls switch
{
[] => "No balls bowled",
[6, ..] => "Started with a six",
[.., 6] => "Ended with a six",
[var first, .., var last] => $"First ball {first}, last ball {last}",
_ => "Just one ball"
};
Console.WriteLine(summary);
}Ended with a six Started with a six First ball 1, last ball 4 No balls bowled
[var first, .., var last] matches any list with at least two elements and names the first and last. The final _ catches what is left: a single-ball list.
Common mistakes
- Forgetting the
_arm. You get warningCS8509, and an unmatched value crashes at run time withSwitchExpressionException. - A broad pattern before a narrow one. If
>= 40comes before>= 90, the later arm can never match, and the compiler reports errorCS8510: The pattern has already been handled by a previous arm of the switch expression. - Missing
breakin a switch statement. ErrorCS0163. Every section needsbreak,returnorthrow. - Mixing up the syntaxes. Statements use
case x:with colons; expressions usex =>with commas between arms and a semicolon after the closing brace. - Writing
>= 6 && <= 9. Inside patterns, use the wordsand,orandnot, not&&and||.
Key points
- A switch statement runs one section; each section ends with
break, and there is no fall-through. - A switch expression
value switch { pattern => result, ... }returns a value. - Patterns include constants, relational (
>= 90), logical (and,or,not), type (int n), property ({ Month: 1 }), tuple ((a, b)) and list ([first, .., last]) patterns. whenadds an extra condition to an arm, and_matches anything.- Arms are checked in order; the compiler warns about missing cases and rejects unreachable ones.
Exercise
A courier company in Kochi charges by weight and zone. Write a switch expression on the tuple (zone, kg) where zone is a string and kg is an int: "local" costs Rs 40 for up to 1 kg and Rs 70 above that; "state" costs Rs 60 up to 1 kg and Rs 110 above; "national" costs Rs 90 up to 1 kg and Rs 180 above. Anything else costs 0. Print the charge for ("state", 3) and ("national", 1).
Show solution
(string zone, int kg)[] parcels = [("state", 3), ("national", 1)];
foreach (var (zone, kg) in parcels)
{
int charge = (zone, kg) switch
{
("local", <= 1) => 40,
("local", _) => 70,
("state", <= 1) => 60,
("state", _) => 110,
("national", <= 1) => 90,
("national", _) => 180,
_ => 0
};
Console.WriteLine($"{zone}, {kg} kg: Rs {charge}");
}state, 3 kg: Rs 110 national, 1 kg: Rs 90
Each tuple arm combines a constant pattern for the zone with a relational pattern for the weight. The <= 1 arm comes before the _ arm for the same zone, so light parcels match first.
Quick Quiz
1. What does int x = 15; string s = x switch { < 10 => "small", < 20 => "medium", _ => "large" }; set s to?
15 < 10 fails, 15 < 20 matches, so the second arm wins.
2. What happens if no arm of a switch expression matches at run time?
A switch expression must produce a value; if nothing matches, an exception is thrown.
3. Which pattern matches an array with at least two elements and captures the last one?
.. matches any number of elements in the middle, so the pattern needs a first element, then the captured last element.
4. In a switch statement, what is required at the end of each case section?
C# does not allow fall-through between sections; each must end with a jump such as break.
Learn C# with a mentor
Free lessons get you started. Our Programming & Software Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
