A switch statement compares one value against a list of constant case labels and runs the section that matches. In C#, every section must end with break (or another statement that leaves it), so one case can never silently run into the next.
Output:
200: OK
302: redirect
404: not found
500: unhandled status
The value in parentheses is evaluated once and compared against each label. default runs when nothing matches; it is optional, and it may appear anywhere in the list, although it usually goes last. If there is no match and no default, the whole switch does nothing.
Every Section Needs a break
In C, C++, Java and JavaScript, forgetting break makes execution slide into the next case. C# turns that mistake into a compile error:
switch (level)
{
case 1:
Console.WriteLine("read");
// error CS0163: Control cannot fall through from one case label ('case 1:') to another
case 2:
Console.WriteLine("write");
break;
}
The rule is that the end of a section's statement list must not be reachable. break is the usual way to satisfy it, but anything that leaves the section works:
returnleaves the whole method, which is often the neatest form inside a small helper.throwraises an exception for a value that should never occur.continuejumps to the next iteration when theswitchsits inside a loop.goto caseandgoto defaultjump to another section on purpose.
The last section is not exempt. A default: at the bottom without break fails with CS8070, Control cannot fall out of switch from final case label ('default:').
Several Values in One Case
Labels with no statements between them share the section below them. This is the one kind of fall-through C# allows, because an empty label cannot hide any code:
Output:
'O' is a vowel
'k' is a consonant or symbol
' ' is a space
'7' is a digit
Each section here ends with return, so no break is needed (a break after a return would be unreachable code). Two labels with the same value are an error, CS0152 The switch statement contains multiple cases with the label value '1', and every label must be a compile-time constant: a variable in a case fails with "a constant value is expected".
Switch on a String
Strings are the most common switch value after integers. The comparison is ordinal and case-sensitive, so normalize the input before switching:
Output:
Starting the server
Stopping the server
Server is running
Unknown command: restart
Unknown command: (none)
Without ToLowerInvariant, "STOP" would reach default. A null value is legal to switch on: it matches no string constant and falls to default, so the switch itself never throws.
Switch on an Enum
Enums are what switch was made for: a closed set of named values.
Output:
Pending: wait for payment
Paid: pack and ship
Shipped: track the parcel
Delivered: nothing, order is closed
Cancelled: nothing, order is closed
ArgumentOutOfRangeException for value 42
The compiler does not check that a switch statement covers every enum member, and an enum variable can hold any integer of its underlying type ((OrderStatus)42 is legal). The default that throws turns a forgotten case into an immediate, obvious failure instead of a silent no-op.
goto case and goto default
When one case really should continue into another, C# makes you write the jump:
Output:
admin:
manage users
edit posts
read posts
editor:
edit posts
read posts
guest:
read posts
goto case takes a constant that must match one of the labels in the same switch. It is rare in real code: shared behavior is usually clearer as a method that several cases call. It is still the answer to "how do I fall through in C#".
Variables Inside Cases
The whole switch block is one scope, so two cases cannot declare a variable with the same name:
switch (shape)
{
case "square":
int area = side * side;
break;
case "rectangle":
int area = width * height; // error CS0128: A local variable or function named 'area' is already defined in this scope
break;
}
Wrap each section's body in braces to give it its own scope: case "square": { int area = side * side; ... break; }.
break Inside a Loop
break in a case leaves the switch, not the loop around it. continue does the opposite: a switch is not a loop, so continue goes to the loop's next iteration.
Output:
end marker found
Total: 134
The loop kept going after "end" and added 99. To stop the loop from inside a switch, set a flag and test it after the switch, or move the loop into a method and return. The break and continue page compares the options.
Pattern Case Labels and when
Since C# 7.0, a case label can be a pattern instead of a constant: it can test the type of the value and add a when condition. This form is common in modern C# code:
static string Describe(object value)
{
switch (value)
{
case null:
return "nothing";
case int n when n < 0:
return $"negative int {n}";
case int n:
return $"int {n}";
case string s when s.Length == 0:
return "empty string";
case string s:
return $"string \"{s}\"";
default:
return $"other: {value.GetType().Name}";
}
}
With patterns, order matters: the first matching case wins, and the compiler reports an error when a case can never be reached because an earlier one already covers it (putting case int n: above case int n when n < 0: fails). The when clause also works on constant cases: case 0 when isAdmin:. C# 9 added relational and logical patterns, so a case can read case >= 90: or case 'a' or 'e' or 'i':.
In C# 7 without patterns, the same type dispatch is an if chain with the is type pattern:
Output:
negative int -3
int 12
empty string
string "hi"
other: Decimal
nothing
When every branch of a switch only computes a value, the switch expression is shorter still: var grade = score switch { >= 90 => "A", >= 80 => "B", _ => "C" };. The switch expression page covers its syntax, patterns and exhaustiveness rules.
What You Can Switch On
| Value type | Constant cases | Notes |
|---|---|---|
int, long, byte and other integers | yes | the classic case |
char | yes | case 'a': |
string | yes | ordinal, case-sensitive, null goes to default |
bool | yes | legal, but an if is clearer |
any enum | yes | no exhaustiveness check |
double, decimal | from C# 7 | constants only; ranges need C# 9 relational patterns |
| any other type | from C# 7 | through type patterns, case Circle c: |
Frequently Asked Questions
Is break required in a C# switch?
Every case section must end in a statement that leaves it: usually break, but return, throw, continue (inside a loop) or goto case also count. Falling off the end of a section into the next one is a compile error, CS0163 Control cannot fall through from one case label ('case 1:') to another, and that includes the last section.
How do I handle multiple values in one C# switch case?
Stack the labels with nothing between them: case "sat": case "sun": Console.WriteLine("weekend"); break;. Empty case labels may fall through to the next label; only a section that contains statements must end in break. In C# 9 and later a pattern case can also say case 1 or 2 or 3:.
Is a C# switch on a string case sensitive?
Yes. String cases are compared with ordinal, case-sensitive equality, so "Yes" does not match case "yes":. Normalize the value first with switch (input.Trim().ToLowerInvariant()). A null string matches no constant case and goes to default.
Can a C# switch fall through like in C or Java?
Not implicitly. When you want one case to continue into another, say so with goto case <value>; or goto default;. This makes every fall-through visible in the code, which is why C# forbids the silent version.
What is the difference between a switch statement and a switch expression in C#?
A switch statement runs statements and needs case, break and default. A switch expression (C# 8 and later) produces a value: var label = n switch { 0 => "zero", > 0 => "positive", _ => "negative" };. Use the expression when every branch only computes a result.