DEV Community

mohamed Tayel
mohamed Tayel

Posted on

c# advance: Switch Expressions

C# introduced switch expressions to enhance the use of patterns and pattern matching, making code more concise and readable. Building on the classic switch statement, switch expressions offer a more fluent syntax by allowing developers to directly return values based on pattern matches. In this article, we’ll dive into the syntax, structure, and real-world usage of switch expressions, showcasing their power through a detailed example. Additionally, we’ll include assignments at three levels of difficulty to help you master this concept.

What is a Switch Expression?

Switch expressions combine the functionality of switch statements with expression-bodied members. Unlike traditional switch statements, which execute blocks of code, switch expressions are used to evaluate and return values directly.

Basic Syntax of Switch Expressions

The basic syntax of a switch expression looks like this:

var result = input switch
{
    pattern1 => expression1,
    pattern2 => expression2,
    _ => defaultExpression // Discard pattern as the default case
};
Enter fullscreen mode Exit fullscreen mode
  • input: The value, tuple, or object being evaluated.
  • pattern: Represents the condition that the input must match.
  • expression: The value returned when the pattern is matched.
  • _ (Discard Pattern): Serves as the default case, capturing any unmatched cases.

Key Features of Switch Expressions

  1. Assigning the Result: The result can be assigned to a variable or returned directly.
  2. Patterns: Switch expressions support simple, complex, and nested patterns.
  3. Consistent Return Types: All expressions must return values of compatible types.
  4. Order of Patterns: More specific patterns must precede general ones to avoid unreachable code errors.

Detailed Example: Calculating Freight Cost with Switch Expressions

Let’s enhance the example to show how switch expressions can be used effectively in a real-world scenario, like calculating freight costs based on different shipping providers and conditions.

Step 1: Define Order and Shipping Provider Classes

We’ll start by defining classes to represent orders and various shipping providers:

public class Order
{
    public IShippingProvider ShippingProvider { get; set; }
    public decimal OrderWeight { get; set; }
}

public interface IShippingProvider { }

public class SwedishPostalServiceShippingProvider : IShippingProvider
{
    public bool IsNextDayDelivery { get; set; }
}

public class DHLShippingProvider : IShippingProvider
{
    public bool IsInternational { get; set; }
}

public class FedExShippingProvider : IShippingProvider
{
    public bool IsExpress { get; set; }
}
Enter fullscreen mode Exit fullscreen mode
  • Order: Represents an order, including the shipping provider and order weight.
  • Shipping Providers: Different providers have specific properties, like next-day delivery, international shipping, or express delivery.

Step 2: Implement the CalculateFreightCost Method

Now, let’s implement the CalculateFreightCost method using a switch expression:

public decimal CalculateFreightCost(Order order)
{
    var freightCost = order.ShippingProvider switch
    {
        // Swedish Postal Service: Free for regular delivery
        SwedishPostalServiceShippingProvider { IsNextDayDelivery: false } => 0,

        // Swedish Postal Service: Higher cost for next-day delivery
        SwedishPostalServiceShippingProvider { IsNextDayDelivery: true } => 25,

        // DHL: Varies based on international shipping
        DHLShippingProvider { IsInternational: true } => 40 + (order.OrderWeight * 1.5m),
        DHLShippingProvider { IsInternational: false } => 20 + (order.OrderWeight * 1.0m),

        // FedEx: Varies based on express shipping
        FedExShippingProvider { IsExpress: true } => 50 + (order.OrderWeight * 2.0m),
        FedExShippingProvider { IsExpress: false } => 30 + (order.OrderWeight * 1.2m),

        // Default case: Other providers with base cost and weight
        _ => 15 + (order.OrderWeight * 1.1m)
    };

    return freightCost;
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Testing the CalculateFreightCost Method

We can create a few test cases to demonstrate how the switch expression works:

var regularOrder = new Order
{
    ShippingProvider = new SwedishPostalServiceShippingProvider { IsNextDayDelivery = false },
    OrderWeight = 5
};

var nextDayOrder = new Order
{
    ShippingProvider = new SwedishPostalServiceShippingProvider { IsNextDayDelivery = true },
    OrderWeight = 3
};

var internationalDHLOrder = new Order
{
    ShippingProvider = new DHLShippingProvider { IsInternational = true },
    OrderWeight = 10
};

var expressFedExOrder = new Order
{
    ShippingProvider = new FedExShippingProvider { IsExpress = true },
    OrderWeight = 8
};

// Calculate freight costs
Console.WriteLine($"Regular Swedish Postal Service: {CalculateFreightCost(regularOrder)}");
Console.WriteLine($"Next-day Swedish Postal Service: {CalculateFreightCost(nextDayOrder)}");
Console.WriteLine($"International DHL: {CalculateFreightCost(internationalDHLOrder)}");
Console.WriteLine($"Express FedEx: {CalculateFreightCost(expressFedExOrder)}");
Enter fullscreen mode Exit fullscreen mode

Explanation

  1. Type and Property Patterns:

    • For SwedishPostalServiceShippingProvider, the expression checks if it is a next-day delivery or not.
    • For DHLShippingProvider, the expression differentiates between international and domestic shipping.
    • For FedExShippingProvider, it evaluates if the delivery is express or standard.
  2. Default Case:

    • The discard pattern (_) handles any unmatched shipping providers by adding a base cost and weight-based cost.
  3. Granularity and Order:

    • The most specific patterns are placed at the top to prevent general patterns from overshadowing them. For example, next-day delivery for the Swedish Postal Service is checked before regular delivery.

Assignments

Easy Level

  1. Task: Modify the CalculateFreightCost method to add a new shipping provider called UPSShippingProvider. Assume that all UPS deliveries have a base cost of 35 plus an additional 1.3 per unit of weight.
    • Hint: Add a new case for UPSShippingProvider within the switch expression.
    • Expected Outcome: Ensure that UPS is included in the freight cost calculation.

Medium Level

  1. Task: Extend the switch expression to handle weekend deliveries for all shipping providers. If it’s a weekend delivery, add an extra charge of 10 to the cost.
    • Hint: Add a new property IsWeekendDelivery to the Order class, and use a combination of type and property patterns in the switch expression.
    • Expected Outcome: The calculation should reflect additional charges for weekend deliveries, regardless of the provider.

Difficult Level

  1. Task: Refactor the CalculateFreightCost method to use nested patterns to differentiate between more complex conditions, such as combining weekend delivery with next-day delivery.
    • Hint: Use nested patterns to handle cases like "next-day delivery on weekends" or "express delivery internationally."
    • Expected Outcome: Your code should correctly calculate different combinations of conditions, using the most granular patterns first.

Final Thoughts

Switch expressions simplify decision-making logic, making code clearer and more maintainable. They offer flexibility with various pattern types (type, property, and recursive patterns), making them ideal for scenarios with complex conditions.

By working through the assignments, you’ll be able to apply switch expressions effectively in your own projects, making your code more robust and easier to maintain. Give it a try and see how it can transform your coding experience!

Top comments (0)