The switch statement in C# is used when a program must choose one path from many possible cases. It is cleaner than a long if else if ladder when the same value is compared against several fixed options. Switch statements are common with menu choices, enum values, command names, status codes, keyboard input, and simple routing logic.
A switch checks one expression and then runs the matching case block. If no case matches, the optional default block runs. This makes the flow easy to scan because all possible branches are grouped under one statement.
What Is Switch Statement in C#?
A switch statement is a selection statement. It evaluates an expression and compares the result with different case labels. When a matching case is found, that case block is executed.
int day = 3;
switch (day)
{
case 1:
Console.WriteLine("Monday");
break;
case 2:
Console.WriteLine("Tuesday");
break;
case 3:
Console.WriteLine("Wednesday");
break;
default:
Console.WriteLine("Invalid day");
break;
}
Here, the value of day is 3, so the third case runs. The break statement exits the switch after the matching block finishes.
Syntax of Switch Statement
switch (expression)
{
case value1:
// code
break;
case value2:
// code
break;
default:
// code when no case matches
break;
}
The expression can be a value such as an integer, character, string, enum, or another type supported by pattern matching. Each case represents a possible value or pattern. The default case is optional but useful when invalid or unexpected input must be handled.
Switch with String Values
C# supports switching on strings. This is useful for commands, menu choices, modes, and small text-based inputs.
string command = "start";
switch (command)
{
case "start":
Console.WriteLine("Starting service");
break;
case "stop":
Console.WriteLine("Stopping service");
break;
case "restart":
Console.WriteLine("Restarting service");
break;
default:
Console.WriteLine("Unknown command");
break;
}
String switches are readable, but input should usually be normalized first. For example, user input may need trimming or case conversion before the switch so Start, start, and start do not behave differently by accident.
Switch with Enum Values
Switch statements work especially well with enums because enums represent a known set of named values. The code becomes readable because each case has a meaningful name.
enum OrderStatus
{
Pending,
Processing,
Shipped,
Delivered,
Cancelled
}
OrderStatus status = OrderStatus.Shipped;
switch (status)
{
case OrderStatus.Pending:
Console.WriteLine("Order received");
break;
case OrderStatus.Shipped:
Console.WriteLine("Order is on the way");
break;
case OrderStatus.Cancelled:
Console.WriteLine("Order cancelled");
break;
default:
Console.WriteLine("No special action");
break;
}
This is much better than switching on raw numbers such as 1, 2, or 3. The enum names explain the business meaning directly.
Break Statement in Switch
In C#, each non-empty case block must normally end with a jump statement such as break, return, throw, or goto. This prevents accidental fall-through, which is a common source of bugs in some other languages.
switch (level)
{
case 1:
Console.WriteLine("Beginner");
break;
case 2:
Console.WriteLine("Intermediate");
break;
}
The break exits the switch immediately. Without it, C# does not allow control to fall into the next non-empty case in the usual way.
Multiple Cases with Same Code
Sometimes multiple case labels should run the same code. In C#, empty cases can be stacked before the shared block.
char grade = 'B';
switch (grade)
{
case 'A':
case 'B':
case 'C':
Console.WriteLine("Passed");
break;
case 'D':
case 'F':
Console.WriteLine("Failed");
break;
default:
Console.WriteLine("Invalid grade");
break;
}
This avoids duplicate code and keeps related cases grouped together. It is useful when several inputs should produce the same result.
Switch Statement vs if else
| Feature | Switch | if else |
|---|---|---|
| Best for | One value matched against many fixed cases | Ranges, complex Boolean expressions, multiple variables |
| Readability | Cleaner for menu-like logic | Cleaner for flexible conditions |
| Example | case OrderStatus.Shipped | age >= 18 && hasId |
| Default path | default | else |
Use switch when the code compares one expression against many known values. Use if else when conditions involve ranges, multiple variables, or more complex rules.
Switch Expression in C#
Modern C# supports switch expressions. A switch expression returns a value and is often shorter than a full switch statement when each case only produces a result.
string message = status switch
{
OrderStatus.Pending => "Order received",
OrderStatus.Shipped => "Order shipped",
OrderStatus.Delivered => "Order delivered",
OrderStatus.Cancelled => "Order cancelled",
_ => "Unknown status"
};
The underscore _ acts like a default pattern. Switch expressions are useful for mapping values, returning labels, calculating simple results, and keeping assignment logic compact.
Pattern Matching in Switch
C# switch supports pattern matching, so cases can check more than simple constants. You can match types, conditions, property patterns, and ranges depending on the C# version being used.
object value = 42;
switch (value)
{
case int number when number > 0:
Console.WriteLine("Positive integer");
break;
case string text:
Console.WriteLine(text.ToUpper());
break;
case null:
Console.WriteLine("Null value");
break;
}
The when keyword adds an extra condition to a case. This makes switch more flexible while still keeping branches organized.
Default Case in Switch
The default case runs when none of the listed cases match. It is similar to the final else in an if else ladder. A default case is not required by syntax, but it is usually a good defensive habit when values can come from users, files, APIs, databases, or future enum changes.
For example, if a command comes from user input, the program should not silently do nothing when the command is unknown. It should show a clear message, log the problem, or throw an exception depending on the context.
switch (command)
{
case "create":
CreateFile();
break;
case "delete":
DeleteFile();
break;
default:
Console.WriteLine("Unsupported command");
break;
}
In production code, the default path is often where validation and error handling become visible. It protects the program from unexpected values and makes debugging easier.
Switch for Command Handling
A common practical use of switch is command handling. A console application, admin panel, game loop, chatbot, or device controller may receive commands and map each command to an action. Switch keeps this mapping readable when the command list is small.
string input = Console.ReadLine()?.Trim().ToLower() ?? "";
switch (input)
{
case "help":
ShowHelp();
break;
case "status":
ShowStatus();
break;
case "exit":
ExitApplication();
break;
default:
Console.WriteLine("Type help to see available commands.");
break;
}
This is clean for a small number of commands. If the number of commands becomes large, a dictionary of command handlers or a command pattern can be better because each command can live in its own class or method.
When Switch Becomes Too Large
A switch statement is readable only while each case stays small and focused. If every case contains many lines of business logic, database calls, validation rules, and UI updates, the switch becomes a maintenance problem. The issue is not the switch syntax itself. The issue is that too much responsibility is stored in one place.
A practical approach is to keep each case as a dispatcher. The case should call a well-named method, and the method should contain the detailed logic. This keeps the switch easy to scan while still allowing each operation to be implemented properly elsewhere.
switch (operation)
{
case Operation.Create:
HandleCreate();
break;
case Operation.Update:
HandleUpdate();
break;
case Operation.Delete:
HandleDelete();
break;
}
This structure is easier to test because each handler can be tested separately. It is also easier to modify because adding details to one operation does not make the entire switch harder to read.
Switch Expression vs Switch Statement
A switch statement is better when each case performs actions. A switch expression is better when each case produces a value. Mixing these responsibilities usually makes code less clear. If the branch prints messages, updates state, writes files, or calls services, a switch statement is usually more natural. If the branch simply returns a string, number, enum, or object, a switch expression can be cleaner.
| Use Case | Better Choice |
|---|---|
| Run different methods | Switch statement |
| Return a label from an enum | Switch expression |
| Validate commands with side effects | Switch statement |
| Map status to color or message | Switch expression |
The goal is not to use the newest syntax everywhere. The goal is to choose the form that makes the decision easiest to read.
Switch with Ranges
Modern C# switch expressions can handle ranges through relational patterns. This is useful for grading, pricing levels, warning thresholds, and classification logic.
int marks = 84;
string grade = marks switch
{
>= 90 => "A",
>= 75 => "B",
>= 60 => "C",
>= 40 => "D",
_ => "F"
};
Order matters here. The highest range is checked first. If >= 40 were placed at the top, almost every passing mark would match it and the later cases would never be reached.
Switch is most effective when it expresses a clear set of alternatives. If the alternatives are stable, named, and easy to list, switch gives the reader a compact map of the possible paths.
For growing applications, keep the decision visible but move heavy work into separate methods. That balance gives you readable branching without creating a giant block of procedural code.
Common Mistakes with Switch Statement
- Using switch for complex range checks where
if elsewould be clearer. - Forgetting to handle unexpected values with
defaultor_. - Duplicating the same logic in many cases instead of grouping cases.
- Switching on raw numbers instead of using enums for meaningful states.
- Letting one switch become too large instead of moving behavior into methods or classes.
Best Practices for Switch Statement in C#
Keep switch cases short, use meaningful enum or string values, handle the default path, and choose switch expressions when the goal is to return a value. If a switch grows too large, consider whether the logic belongs in separate methods, a dictionary of handlers, or polymorphic classes.
Continue learning C# in order
Follow the topic sequence with the previous and next lesson.