Skip to main content

C# Switch Expressions

Switch expressions are a modern C# feature introduced in C# 8.0 that provide a more concise and expressive way to perform conditional logic compared to traditional switch statements. They're especially useful when you need to transform data based on conditions or when pattern matching against different types.

Introduction to Switch Expressions

Traditional switch statements in C# have been around since the beginning, but they can be verbose and repetitive. Switch expressions offer a more elegant, expression-based alternative that's particularly useful when you want to:

  • Assign a value based on a condition
  • Transform data based on different patterns
  • Return different values depending on an input

Let's explore how switch expressions work and how they differ from standard switch statements.

Basic Syntax

Here's the basic syntax of a switch expression:

csharp
result = someValue switch
{
pattern1 => expression1,
pattern2 => expression2,
_ => defaultExpression
};

This reads as: "Evaluate someValue, and depending on which pattern it matches, return the corresponding expression."

Let's see a simple example:

csharp
string GetDayType(DayOfWeek day) => day switch
{
DayOfWeek.Saturday => "Weekend",
DayOfWeek.Sunday => "Weekend",
_ => "Weekday"
};

// Usage
Console.WriteLine(GetDayType(DateTime.Now.DayOfWeek));

If today is Tuesday, the output would be:

Weekday

If today is Saturday, the output would be:

Weekend

Switch Expressions vs Switch Statements

To understand the advantages of switch expressions, let's compare them to traditional switch statements:

Traditional Switch Statement

csharp
string GetDayType(DayOfWeek day)
{
switch (day)
{
case DayOfWeek.Saturday:
return "Weekend";
case DayOfWeek.Sunday:
return "Weekend";
default:
return "Weekday";
}
}

Modern Switch Expression

csharp
string GetDayType(DayOfWeek day) => day switch
{
DayOfWeek.Saturday => "Weekend",
DayOfWeek.Sunday => "Weekend",
_ => "Weekday"
};

The switch expression is:

  • More concise (fewer lines of code)
  • Expression-based (returns a value directly)
  • Uses => arrow syntax instead of case and return
  • Uses _ as the discard pattern (similar to default)

Pattern Matching in Switch Expressions

One of the most powerful aspects of switch expressions is their ability to use pattern matching. C# supports several patterns:

Constant Patterns

Matching against constant values:

csharp
string GetTrafficLightAction(string color) => color.ToLower() switch
{
"red" => "Stop",
"yellow" => "Caution",
"green" => "Go",
_ => "Invalid color"
};

// Usage
Console.WriteLine(GetTrafficLightAction("Red")); // Output: Stop
Console.WriteLine(GetTrafficLightAction("Green")); // Output: Go

Type Patterns

Matching based on the type of an object:

csharp
string GetObjectType(object obj) => obj switch
{
int i => $"Integer: {i}",
string s => $"String: {s}",
DateTime d => $"Date: {d.ToShortDateString()}",
null => "Null value",
_ => $"Unknown type: {obj.GetType().Name}"
};

// Usage
Console.WriteLine(GetObjectType(42)); // Output: Integer: 42
Console.WriteLine(GetObjectType("Hello")); // Output: String: Hello
Console.WriteLine(GetObjectType(DateTime.Now)); // Output: Date: [current date]

Property Patterns

Matching based on object properties:

csharp
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string Country { get; set; }
}

string GetPersonCategory(Person person) => person switch
{
{ Age: < 18 } => "Minor",
{ Age: >= 65 } => "Senior",
{ Country: "USA" } => "American Adult",
_ => "Adult"
};

// Usage
var alice = new Person { Name = "Alice", Age = 25, Country = "USA" };
var bob = new Person { Name = "Bob", Age = 17, Country = "Canada" };
var carol = new Person { Name = "Carol", Age = 70, Country = "UK" };

Console.WriteLine(GetPersonCategory(alice)); // Output: American Adult
Console.WriteLine(GetPersonCategory(bob)); // Output: Minor
Console.WriteLine(GetPersonCategory(carol)); // Output: Senior

Tuple Patterns

Matching against multiple values simultaneously using tuples:

csharp
string GetQuadrant(int x, int y) => (x, y) switch
{
(0, 0) => "Origin",
(> 0, > 0) => "First quadrant",
(< 0, > 0) => "Second quadrant",
(< 0, < 0) => "Third quadrant",
(> 0, < 0) => "Fourth quadrant",
(_, 0) => "X-axis",
(0, _) => "Y-axis",
_ => "Impossible" // This case will never be reached
};

// Usage
Console.WriteLine(GetQuadrant(5, 3)); // Output: First quadrant
Console.WriteLine(GetQuadrant(-2, 4)); // Output: Second quadrant
Console.WriteLine(GetQuadrant(0, 0)); // Output: Origin
Console.WriteLine(GetQuadrant(0, -7)); // Output: Y-axis

Real-World Applications

Example 1: Calculating Shipping Costs

csharp
decimal CalculateShippingCost(string country, decimal orderValue) => (country, orderValue) switch
{
("USA", < 50m) => 5.99m,
("USA", _) => 0m, // Free shipping for USA orders over $50
("Canada", < 100m) => 10.99m,
("Canada", _) => 5.99m,
("Mexico", _) => 12.99m,
(_, < 100m) => 19.99m,
_ => 14.99m // International orders over $100
};

// Usage
Console.WriteLine($"Shipping to USA for $45: ${CalculateShippingCost("USA", 45m)}");
Console.WriteLine($"Shipping to USA for $65: ${CalculateShippingCost("USA", 65m)}");
Console.WriteLine($"Shipping to France for $120: ${CalculateShippingCost("France", 120m)}");

// Output:
// Shipping to USA for $45: $5.99
// Shipping to USA for $65: $0
// Shipping to France for $120: $14.99

Example 2: Exception Handling with Pattern Matching

csharp
string FormatException(Exception ex) => ex switch
{
FileNotFoundException fnf => $"File not found: {fnf.FileName}",
HttpRequestException { StatusCode: System.Net.HttpStatusCode.NotFound } => "404 - Resource not found",
HttpRequestException { StatusCode: System.Net.HttpStatusCode.Unauthorized } => "401 - Authentication required",
HttpRequestException http => $"HTTP error: {http.StatusCode}",
JsonException => "Invalid JSON format",
null => "No exception occurred",
_ => $"Unexpected error: {ex.Message}"
};

// Usage (in practice this would be used in a catch block)
try
{
// Some code that might throw exceptions
throw new FileNotFoundException("Could not find settings file", "settings.json");
}
catch (Exception ex)
{
Console.WriteLine(FormatException(ex)); // Output: File not found: settings.json
}

Example 3: State Machine

Here's a simple state machine for a vending machine using switch expressions:

csharp
enum VendingMachineState { Ready, ItemSelected, PaymentProcessing, DeliveringItem }
enum UserAction { SelectItem, InsertMoney, Cancel, TakeItem }

VendingMachineState GetNextState(VendingMachineState currentState, UserAction action) =>
(currentState, action) switch
{
(VendingMachineState.Ready, UserAction.SelectItem) => VendingMachineState.ItemSelected,
(VendingMachineState.ItemSelected, UserAction.InsertMoney) => VendingMachineState.PaymentProcessing,
(VendingMachineState.ItemSelected, UserAction.Cancel) => VendingMachineState.Ready,
(VendingMachineState.PaymentProcessing, UserAction.Cancel) => VendingMachineState.Ready,
(VendingMachineState.PaymentProcessing, _) => VendingMachineState.DeliveringItem,
(VendingMachineState.DeliveringItem, UserAction.TakeItem) => VendingMachineState.Ready,
_ => currentState // Invalid state transition, stay in current state
};

// Usage: simulate a successful purchase
var state = VendingMachineState.Ready;
Console.WriteLine($"Initial state: {state}");

state = GetNextState(state, UserAction.SelectItem);
Console.WriteLine($"After selecting item: {state}");

state = GetNextState(state, UserAction.InsertMoney);
Console.WriteLine($"After inserting money: {state}");

state = GetNextState(state, UserAction.TakeItem);
Console.WriteLine($"After taking item: {state}");

// Output:
// Initial state: Ready
// After selecting item: ItemSelected
// After inserting money: PaymentProcessing
// After taking item: DeliveringItem

Best Practices for Switch Expressions

  1. Keep it readable: Just because you can put complex logic in a pattern, doesn't mean you should. Consider readability.

  2. Order matters: More specific patterns should come before more general ones, as patterns are evaluated in order.

  3. Use when guards for complex conditions: For more complex conditions, use when clauses:

csharp
var result = number switch
{
int n when n > 0 && n % 2 == 0 => "Positive even number",
int n when n > 0 => "Positive odd number",
0 => "Zero",
_ => "Negative number"
};
  1. Be exhaustive: Make sure your patterns cover all possible input values or include a discard (_) pattern.

Common Mistakes and Pitfalls

  1. Forgetting the discard pattern: Always include a _ case to handle unexpected inputs, unless you want an exception to be thrown.

  2. Order of patterns: Remember that patterns are evaluated in order, so more specific patterns should come first.

  3. Missing comma after a pattern arm: Each pattern arm must be followed by a comma, except for the last one.

  4. Using break or return: Unlike switch statements, switch expressions don't use break or return keywords.

Summary

Switch expressions provide a modern, concise way to handle conditional logic in C#. They offer several advantages over traditional switch statements:

  • More compact syntax
  • Expression-based (returns values directly)
  • Powerful pattern matching capabilities
  • Support for tuple patterns for multi-value matching
  • Property patterns for object property matching

These features make switch expressions an excellent choice for many conditional logic scenarios, especially when transforming data or implementing pattern-based logic.

Exercises

  1. Write a switch expression that calculates a discount based on order total:

    • Orders under $50: no discount
    • Orders 5050-100: 5% discount
    • Orders 100100-200: 10% discount
    • Orders over $200: 15% discount
  2. Create a GetGrade function that converts a numeric score to a letter grade using a switch expression:

    • 90-100: "A"
    • 80-89: "B"
    • 70-79: "C"
    • 60-69: "D"
    • Below 60: "F"
  3. Write a switch expression that handles different shapes and calculates their area (hint: use type patterns).

Additional Resources



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)