C# > Functional Programming > Pattern Matching > switch Expression with Patterns

Switch Expression with Pattern Matching in C#

This snippet demonstrates the use of switch expressions combined with pattern matching in C#. Switch expressions provide a concise way to evaluate an expression and return a value based on different patterns. This is a powerful feature for handling complex logic in a clean and readable manner.

Basic Switch Expression with Type Pattern

This example showcases a simple switch expression that uses type patterns to determine the type of the 'shape' object. Based on the type (Circle or Rectangle), it returns a descriptive string. The 'null' case handles null objects, and the discard pattern '_' acts as a default case for any other type. This dramatically simplifies what would otherwise be a series of 'if-else' statements.

public static string GetShapeType(object shape)
{
    return shape switch
    {
        Circle c => $"This is a Circle with radius {c.Radius}",
        Rectangle r => $"This is a Rectangle with width {r.Width} and height {r.Height}",
        null => "Shape is null",
        _ => "Unknown shape"
    };
}

public class Circle { public double Radius { get; set; } }
public class Rectangle { public double Width { get; set; } public double Height { get; set; } }

Property Pattern Matching

This example uses property patterns within the switch expression. It checks the properties 'TotalAmount' and 'IsNewCustomer' of the 'Order' object to determine the appropriate discount. The patterns allow you to specify conditions on properties directly within the switch cases, making the logic clear and easy to understand.

public static string GetDiscount(Order order)
{
    return order switch
    {
        { TotalAmount: > 1000, IsNewCustomer: true } => "20% discount",
        { TotalAmount: > 500 } => "10% discount",
        { IsNewCustomer: true } => "5% discount",
        _ => "No discount"
    };
}

public class Order { public decimal TotalAmount { get; set; } public bool IsNewCustomer { get; set; } }

Positional Pattern Matching

This example demonstrates positional pattern matching with records. The 'Order' is defined as a record, allowing you to deconstruct it into its positional parameters within the switch expression. This makes the code more concise and readable when dealing with records or tuples.

public static string GetOrderStatus(Order order)
{
    return order switch
    {
        (> 1000, true) => "High Value New Customer",
        (> 500, false) => "Valued Customer",
        _ => "Regular Customer"
    };
}

public record Order(decimal TotalAmount, bool IsNewCustomer);

Concepts Behind the Snippet

Pattern Matching: Pattern matching allows you to match expressions based on their structure or properties, rather than just their value. It offers a concise and readable way to handle complex conditional logic.
Switch Expressions: Switch expressions provide a more compact and expressive syntax compared to traditional switch statements. They return a value based on the matched pattern.
Records: Records are a concise way to define immutable data structures, often used with positional pattern matching.

Real-Life Use Case

Consider a scenario where you're building an e-commerce application. You can use switch expressions with pattern matching to calculate shipping costs based on the destination country, weight of the package, and value of the order. Different countries might have different shipping rates, and you can easily define these rules using pattern matching.

Best Practices

  • Be Exhaustive: Ensure that your switch expression covers all possible cases. Use the discard pattern '_' as a default case to handle unexpected inputs.
  • Keep it Readable: Avoid overly complex patterns that can make the code difficult to understand.
  • Use Records When Appropriate: Records are ideal for positional pattern matching because they provide a concise way to define data structures.
  • Test Thoroughly: Test all possible input scenarios to ensure your pattern matching logic is correct.

Interview Tip

Be prepared to explain the benefits of switch expressions with pattern matching over traditional 'if-else' statements. Highlight the increased readability, conciseness, and expressiveness. Also, understand the different types of patterns (type patterns, property patterns, positional patterns) and when to use them.

When to Use Them

Use switch expressions with pattern matching when you have complex conditional logic based on the structure or properties of an object. They are particularly useful when dealing with discriminated unions or data transfer objects.

Memory Footprint

The memory footprint of switch expressions with pattern matching is generally comparable to that of traditional 'if-else' statements. The compiler optimizes the code to minimize overhead. Records can have a slightly smaller memory footprint than traditional classes due to their value-based equality.

Alternatives

The primary alternative is using a series of 'if-else' statements. However, this can become cumbersome and less readable for complex logic. Another alternative is the visitor pattern, but it's often more complex to implement.

Pros

  • Readability: Switch expressions with pattern matching are more readable and concise than traditional 'if-else' statements.
  • Expressiveness: They allow you to express complex conditional logic in a clear and intuitive way.
  • Maintainability: The code is easier to maintain and modify because the logic is well-organized and structured.

Cons

  • Complexity: Overly complex patterns can make the code difficult to understand.
  • Learning Curve: Developers unfamiliar with pattern matching may need some time to learn the syntax and concepts.

FAQ

  • What is the difference between a switch statement and a switch expression?

    A switch statement is a traditional control flow statement that executes a block of code based on the value of an expression. A switch expression, on the other hand, is an expression that evaluates to a value based on pattern matching. Switch expressions are more concise and expressive.
  • What is the discard pattern '_ ' in a switch expression?

    The discard pattern '_' is a wildcard that matches any value. It's typically used as the default case in a switch expression to handle unexpected or unhandled inputs.
  • Are switch expressions with pattern matching more performant than 'if-else' statements?

    In most cases, the performance is comparable. The compiler optimizes both approaches. However, switch expressions with pattern matching can sometimes be slightly faster due to better branch prediction.