Easy Learn C#

C# Switch Statements

Introduction to Switch Statements

The switch statement provides a way to select one of many code blocks to execute based on the value of a single expression. It's particularly useful when you have multiple potential values to test against, as an alternative to complex if-else-if chains.

Key Switch Statement Concepts:

  • Efficiently tests a variable against a list of values (cases)
  • Each case needs to end with a break statement (except when using fall-through)
  • The default case handles all values not explicitly matched
  • In C# 8.0 and later, enhanced pattern matching expands switch capabilities
  • Switch expressions (C# 8.0+) offer a more concise alternative to switch statements

Basic Switch Statement

The traditional switch statement evaluates an expression once and compares it with a series of case values.

Basic Switch Statement Syntax:


switch (expression)
{
    case value1:
        // Code to execute if expression equals value1
        break;
    case value2:
        // Code to execute if expression equals value2
        break;
    // Additional cases as needed
    default:
        // Code to execute if expression doesn't match any case
        break;
}
                            

Key parts of a switch statement:

  • The switch keyword followed by an expression in parentheses
  • Multiple case clauses, each with a specific value and code to execute
  • The break statement to exit the switch block after a matching case
  • An optional default clause that executes when no cases match

Switch Statement Example:


int day = 3;
string dayName;

switch (day)
{
    case 1:
        dayName = "Monday";
        break;
    case 2:
        dayName = "Tuesday";
        break;
    case 3:
        dayName = "Wednesday";
        break;
    case 4:
        dayName = "Thursday";
        break;
    case 5:
        dayName = "Friday";
        break;
    case 6:
        dayName = "Saturday";
        break;
    case 7:
        dayName = "Sunday";
        break;
    default:
        dayName = "Invalid day";
        break;
}

Console.WriteLine($"Day {day} is {dayName}");  // Output: "Day 3 is Wednesday"
                            

Multiple Case Labels

You can include multiple case labels for the same code block when you want the same action for different values:

Multiple Case Labels Example:


char grade = 'B';
string feedback;

switch (grade)
{
    case 'A':
        feedback = "Excellent work!";
        break;
    case 'B':
    case 'C':
        feedback = "Good job!";
        break;
    case 'D':
        feedback = "You need to improve.";
        break;
    case 'F':
        feedback = "Failed. Please try again.";
        break;
    default:
        feedback = "Invalid grade.";
        break;
}

Console.WriteLine(feedback);  // Output: "Good job!"
                            

In this example, both 'B' and 'C' grades produce the same feedback message. This is more concise than duplicating the same code in separate case blocks.

Switch with Strings

C# switch statements can work with strings, making them useful for command processing or text-based input:

String Switch Example:


string command = "exit";
string message;

switch (command.ToLower())
{
    case "start":
        message = "Starting the application...";
        break;
    case "stop":
        message = "Stopping the application...";
        break;
    case "restart":
        message = "Restarting the application...";
        break;
    case "exit":
    case "quit":
        message = "Exiting the application...";
        break;
    default:
        message = "Command not recognized.";
        break;
}

Console.WriteLine(message);  // Output: "Exiting the application..."
                            

Note the use of ToLower() to make the comparison case-insensitive. Be aware that string comparisons in switch statements are case-sensitive by default.

Switch Fall-Through (goto case)

Unlike some other languages, C# doesn't allow automatic fall-through between cases. However, you can use goto case to explicitly transfer control to another case:

Switch with goto case Example:


int option = 2;
string result;

switch (option)
{
    case 1:
        Console.WriteLine("Option 1 selected");
        result = "Processing option 1";
        break;
    case 2:
        Console.WriteLine("Option 2 selected");
        result = "Setting up for option 2";
        goto case 4;  // Explicitly go to case 4 after executing case 2 code
    case 3:
        Console.WriteLine("Option 3 selected");
        result = "Processing option 3";
        break;
    case 4:
        Console.WriteLine("Performing common processing");
        result += " with additional processing";
        break;
    default:
        result = "Invalid option";
        break;
}

// Output:
// "Option 2 selected"
// "Performing common processing"
                            

The goto case statement is useful when multiple cases need to share some common code, but also need their own specific processing. Use it sparingly, as excessive use can make code harder to follow.

Pattern Matching in Switch Statements (C# 7.0+)

C# 7.0 introduced pattern matching in switch statements, allowing for more advanced switch capabilities:

Type Pattern Matching:


object item = "Hello, World!";
string result;

switch (item)
{
    case int i:
        result = $"Integer: {i}";
        break;
    case string s:
        result = $"String: {s}";
        break;
    case bool b:
        result = $"Boolean: {b}";
        break;
    case DateTime d:
        result = $"Date: {d.ToShortDateString()}";
        break;
    case null:
        result = "Null value";
        break;
    default:
        result = $"Other type: {item.GetType().Name}";
        break;
}

Console.WriteLine(result);  // Output: "String: Hello, World!"
                            

Type pattern matching allows you to:

  • Check the runtime type of an expression
  • Cast the value to that type
  • Assign it to a new variable (like i, s, b, and d in the example)

Pattern Matching with When Clause:


object value = 42;
string category;

switch (value)
{
    case int i when i < 0:
        category = "Negative integer";
        break;
    case int i when i == 0:
        category = "Zero";
        break;
    case int i when i > 0 && i <= 10:
        category = "Small positive integer";
        break;
    case int i when i > 10 && i <= 100:
        category = "Medium positive integer";
        break;
    case int i when i > 100:
        category = "Large positive integer";
        break;
    case string s when s.Length == 0:
        category = "Empty string";
        break;
    case string s:
        category = $"String with length {s.Length}";
        break;
    default:
        category = "Other type";
        break;
}

Console.WriteLine(category);  // Output: "Medium positive integer"
                            

The when clause allows you to specify additional conditions for pattern matching, enabling more complex logic within the switch statement.

Switch Expressions (C# 8.0+)

C# 8.0 introduced switch expressions, a more concise way to write switch statements, particularly useful for simple value assignments:

Basic Switch Expression Syntax:


result = expression switch
{
    pattern1 => expression1,
    pattern2 => expression2,
    pattern3 => expression3,
    _ => defaultExpression
};
                            

Key differences from traditional switch statements:

  • The switch keyword follows the expression being tested
  • Uses => (lambda arrow) instead of : and break
  • Uses _ (discard pattern) for the default case
  • Each pattern-expression pair is separated by commas
  • The entire expression must be terminated with a semicolon

Switch Expression Examples:


// Basic switch expression
int day = 3;
string dayName = day switch
{
    1 => "Monday",
    2 => "Tuesday",
    3 => "Wednesday",
    4 => "Thursday",
    5 => "Friday",
    6 => "Saturday",
    7 => "Sunday",
    _ => "Invalid day"
};

Console.WriteLine(dayName);  // Output: "Wednesday"

// Switch expression with pattern matching
object item = 42;
string description = item switch
{
    null => "Nothing",
    int i when i < 0 => "Negative",
    int i when i == 0 => "Zero",
    int i => $"Positive integer: {i}",
    string s => $"String: {s}",
    _ => $"Something else: {item.GetType().Name}"
};

Console.WriteLine(description);  // Output: "Positive integer: 42"
                            

Tuple Patterns in Switch Expressions (C# 8.0+)

Switch expressions can work with tuples, allowing you to match against multiple values simultaneously:

Tuple Pattern Examples:


string GetQuadrant(int x, int y) => (x, y) switch
{
    (0, 0) => "Origin",
    (> 0, > 0) => "Quadrant 1",
    (< 0, > 0) => "Quadrant 2", 
    (< 0, < 0) => "Quadrant 3",
    (> 0, < 0) => "Quadrant 4",
    (0, _) => "Y-axis",
    (_, 0) => "X-axis",
    _ => "Unknown"
};

Console.WriteLine(GetQuadrant(5, 10));   // Output: "Quadrant 1"
Console.WriteLine(GetQuadrant(-5, 10));  // Output: "Quadrant 2"
Console.WriteLine(GetQuadrant(0, 0));    // Output: "Origin"
Console.WriteLine(GetQuadrant(0, 5));    // Output: "Y-axis"

// Matching state transitions
enum State { Inactive, Active, Suspended }
enum Action { Activate, Suspend, Resume, Deactivate }

State GetNextState(State current, Action action) => (current, action) switch
{
    (State.Inactive, Action.Activate) => State.Active,
    (State.Active, Action.Suspend) => State.Suspended,
    (State.Active, Action.Deactivate) => State.Inactive,
    (State.Suspended, Action.Resume) => State.Active,
    (State.Suspended, Action.Deactivate) => State.Inactive,
    _ => current  // Default: stay in current state
};

// Usage
State userState = State.Inactive;
userState = GetNextState(userState, Action.Activate);
Console.WriteLine(userState);  // Output: Active
                            

Relational patterns with >, <, >=, and <= were introduced in C# 9.0 and can be used with tuple patterns for comparing numeric values.

Property Patterns (C# 8.0+)

Property patterns allow you to match based on the properties of an object:

Property Pattern Examples:


// Simple class for the example
class Point
{
    public int X { get; }
    public int Y { get; }

    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }
}

// Using property patterns
string GetPointDescription(Point point) => point switch
{
    { X: 0, Y: 0 } => "Origin",
    { X: 0 } => "On Y-axis",
    { Y: 0 } => "On X-axis",
    { X: > 0, Y: > 0 } => "Quadrant 1",
    { X: < 0, Y: > 0 } => "Quadrant 2",
    { X: < 0, Y: < 0 } => "Quadrant 3",
    { X: > 0, Y: < 0 } => "Quadrant 4",
    _ => "Unknown"
};

var point = new Point(10, 5);
Console.WriteLine(GetPointDescription(point));  // Output: "Quadrant 1"

// Using nested property patterns
class Address
{
    public string Street { get; set; }
    public string City { get; set; }
    public string ZipCode { get; set; }
    public string Country { get; set; }
}

class Customer
{
    public string Name { get; set; }
    public Address ShippingAddress { get; set; }
}

string GetShippingZone(Customer customer) => customer switch
{
    { ShippingAddress: { Country: "USA", ZipCode: var zip } } when zip.StartsWith("9") => "West Coast",
    { ShippingAddress: { Country: "USA", ZipCode: var zip } } when zip.StartsWith("1") => "East Coast",
    { ShippingAddress: { Country: "USA" } } => "Other US",
    { ShippingAddress: { Country: "Canada" } } => "Canada",
    { ShippingAddress: null } => "No shipping address",
    _ => "International"
};
                            

Property patterns provide a concise way to test multiple properties simultaneously, making complex object matching more readable.

Switch vs. If-Else

When should you choose switch over if-else, and vice versa?

Comparison of Switch and If-Else:

Use Switch When Use If-Else When
Testing a single variable against multiple discrete values Testing multiple unrelated variables or conditions
The values are constants known at compile time Using complex conditions with ranges or combinations
You need to match on types or patterns (with C# 7.0+) You need to evaluate boolean expressions directly
You want better performance with many discrete cases You have only a few conditions to check
The code is more readable with switch (often with enums) The logic doesn't cleanly map to discrete cases

Example of a situation better suited for switch:


// Good for switch (enum with discrete values)
enum Day { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday }

Day today = Day.Wednesday;

switch (today)
{
    case Day.Monday:
    case Day.Tuesday:
    case Day.Wednesday:
    case Day.Thursday:
    case Day.Friday:
        Console.WriteLine("It's a weekday.");
        break;
    case Day.Saturday:
    case Day.Sunday:
        Console.WriteLine("It's the weekend!");
        break;
}
                                

Example of a situation better suited for if-else:


// Better for if-else (multiple conditions, ranges)
int age = 25;
double income = 50000;
bool hasLoan = true;

if (age < 18)
{
    Console.WriteLine("Youth rate applies.");
}
else if (age >= 65 || (income < 30000 && !hasLoan))
{
    Console.WriteLine("Discount rate applies.");
}
else if (income > 100000)
{
    Console.WriteLine("Premium rate applies.");
}
else
{
    Console.WriteLine("Standard rate applies.");
}
                                

Best Practices for Switch Statements

Follow these guidelines to write effective and maintainable switch statements:

Switch Statement Best Practices:


// DO: Include a default case
switch (status)
{
    case Status.Pending:
        // Handle pending status
        break;
    case Status.Approved:
        // Handle approved status
        break;
    case Status.Rejected:
        // Handle rejected status
        break;
    default:
        // Handle unrecognized status
        break;
}

// DO: Group related cases
switch (keyPressed)
{
    case ConsoleKey.UpArrow:
    case ConsoleKey.W:
        MoveUp();
        break;
    case ConsoleKey.DownArrow:
    case ConsoleKey.S:
        MoveDown();
        break;
    case ConsoleKey.LeftArrow:
    case ConsoleKey.A:
        MoveLeft();
        break;
    case ConsoleKey.RightArrow:
    case ConsoleKey.D:
        MoveRight();
        break;
    default:
        // Handle other keys
        break;
}

// DON'T: Forget to break or use return/throw
// This will cause a compile error in C#
switch (option)
{
    case 1:
        Console.WriteLine("Option 1");
        // Missing break - C# will generate an error
    case 2:
        Console.WriteLine("Option 2");
        break;
}

// DO: Use switch expressions for simple value assignments
// Instead of:
string GetMonthName(int month)
{
    switch (month)
    {
        case 1: return "January";
        case 2: return "February";
        // ... and so on
        default: return "Invalid month";
    }
}

// Better (C# 8.0+):
string GetMonthName(int month) => month switch
{
    1 => "January",
    2 => "February",
    // ... and so on
    _ => "Invalid month"
};
                            

Additional tips:

  • Consider using enums instead of magic numbers for better readability
  • Extract complex case logic to separate methods
  • Use pattern matching for more sophisticated switching logic
  • Consider switch expressions for concise, expression-based logic
  • Order cases from most to least specific when using pattern matching