Introduction
When writing conditional logic in C#, developers often face choices about the most appropriate control structures to use. Among these options, switch constructs stand out as powerful tools for handling multiple conditions based on a single value. With the evolution of C# and .NET, we now have two distinct approaches: the traditional switch statement and the more modern switch expression introduced in C# 8. This article explores both approaches, clarifying their syntax, benefits, limitations, and ideal use cases to help you make informed decisions in your code.
Switch Statements: The Traditional Approach
Switch statements have been a fundamental part of C# since its inception, providing a clean alternative to lengthy if-else chains when comparing a single value against multiple possibilities:

public static string GetDayType(int day)
{
    switch (day)
    {
        case 1:
        case 7:
            return "Weekend";
        case 2:
        case 3:
        case 4:
        case 5:
        case 6:
            return "Weekday";
        default:
            return "Invalid day";
    }
}

When to use switch statements:

When you need to execute multiple lines of code for each case
When you have complex logic within each case
When you need to use control flow statements like continue or break
When working with side effects (like modifying variables outside the switch)
In scenarios where readability of complex branching logic is important

Potential issues:

Forgetting break statements can cause unexpected "fall-through" behavior where execution continues to the next case
More verbose syntax requires more code
Can be harder to scan and understand at a glance
Prone to errors if proper flow control isn't maintained

Switch Expressions: The Modern Alternative
C# 8 introduced switch expressions as part of the language's movement toward more functional programming paradigms, offering a more concise and expressive syntax:

public static string GetDayTypeExpression(int day) => day switch
{
    1 or 7 => "Weekend",
    >= 2 and <= 6 => "Weekday",
    _ => "Invalid day"
};

When to use switch expressions:

For simple value mappings where each case returns a single result
When using pattern matching features (type patterns, property patterns, etc.)
When you want more readable, compact code
For functional programming approaches where expressions are preferred over statements
When you want compiler enforcement of exhaustiveness (handling all possible cases)

Limitations:

Cannot execute multiple statements per case (single expression only)
Not suitable for complex logic requiring multiple operations
Limited ability to handle side effects
Relatively new feature that may not be familiar to all team members

Tuple Patterns in Switch Expressions
One of the most powerful features of switch expressions is their ability to match on tuples, allowing you to evaluate multiple values simultaneously:

public static string ClassifyPoint(int x, int y) => (x, y) switch
{
    (0, 0) => "Origin",
    (var x1, 0) => $"X-axis at {x1}",
    (0, var y1) => $"Y-axis at {y1}",
    (> 0, > 0) => "First quadrant",
    (< 0, > 0) => "Second quadrant",
    (< 0, < 0) => "Third quadrant",
    (> 0, < 0) => "Fourth quadrant",
    _ => "Error"
};

Tuple patterns allow for extremely concise and readable code when dealing with multiple conditions. You can also use discard patterns (_) for values you don't need:

public static string GetShippingRate(string country, decimal weight) => (country, weight) switch
{
    ("USA", < 1) => "$5.00",
    ("USA", < 5) => "$10.00",
    ("USA", _) => "$15.00",
    ("Canada", < 1) => "$8.00",
    ("Canada", < 5) => "$15.00",
    ("Canada", _) => "$20.00",
    (_, < 1) => "$12.00",
    (_, < 5) => "$25.00",
    _ => "$35.00"
};

Key Differences at a Glance
Switch statements and switch expressions differ in several important ways. While switch statements use more verbose syntax, switch expressions are concise and expression-based. Switch statements can either return a value or not, but switch expressions always return a value. Pattern matching capabilities are limited in switch statements but extensively supported in switch expressions. Switch statements support multiple operations within a case, whereas switch expressions are limited to a single expression per case. Exhaustiveness is not enforced in switch statements, but the compiler checks all cases in switch expressions. Fall-through behavior is possible (and sometimes problematic) in switch statements but not possible in switch expressions. Tuple support is limited in traditional switch statements but excellent in switch expressions. Finally, switch statements have been part of C# since its initial release, while switch expressions were introduced in C# 8 in 2019.
Advanced Pattern Matching with Switch Expressions
One of the most powerful aspects of switch expressions is their enhanced pattern matching capabilities:

public static string DescribeObject(object obj) => obj switch
{
    null => "It's null",
    string s when s.Length == 0 => "Empty string",
    string s => $"String with length {s.Length}",
    int n when n < 0 => "Negative number",
    int n => $"Positive number: {n}",
    Person { Age: > 18 } p => $"Adult named {p.Name}",
    Person p => $"Minor named {p.Name}",
    _ => "Unknown type"
};

This example shows how switch expressions can match on types, properties, and conditions in a highly readable format that would be much more verbose with traditional switch statements.
Performance Considerations
From a performance perspective, both switch statements and expressions generally compile to similar IL code in simple cases. The compiler optimizes switch constructs (both types) into efficient jump tables when possible, particularly for consecutive integer values. For more complex pattern matching scenarios, switch expressions might generate slightly different code, but the performance difference is typically negligible in most applications.
Conclusion
Both switch statements and switch expressions are valuable tools in a C# developer's toolkit, each with their own strengths and appropriate use cases. Switch statements continue to excel in scenarios requiring complex multi-line logic or specific control flow, while switch expressions shine when clarity, conciseness, and functional programming principles are prioritized.
The introduction of switch expressions with tuple support represents a significant advancement in C#'s pattern matching capabilities. This feature allows developers to write more elegant, readable code when evaluating multiple conditions simultaneously, something that would require nested if statements or more complex logic in traditional approaches.
As your codebase evolves, consider gradually adopting switch expressions where they make sense, particularly for pattern matching and tuple-based conditions. By choosing the right construct for each situation, you can write code that is both efficient and expressive, taking full advantage of C#'s rich feature set while maintaining readability and maintainability.