C# 8.0 Features (Part 2)

Part 1Part 3

  1. Switch Syntax
  2. Property Patterns
  3. Default Interface Implementation

1. Switch Syntax

C# 8.0 introduces some syntactic sugar for switch statements to make them shorter and more readable. The best way to demonstrate this is to refactor a C# 7 method into C# 8.0.

Consider 3 different order statuses: Pending, Confirmed and Shipped

public enum OrderStatus
{
    Pending = 1,
    Confirmed = 2,
    Shipped = 3
}

We have defined a method that returns the message to be displayed to the user when they try to cancel an order:

public string GetCancelOrderMessage(OrderStatus status)
{
    switch (status)
    {
        case OrderStatus.Pending:
            return "Order cancelled";
        case OrderStatus.Confirmed:
            return "Please call us";
        case OrderStatus.Shipped:
            return "Cancellation failed";
        default:
            return "Invalid order status";
    }
}

We can significantly reduce the amount of code and improve readability by refactoring to the new C# 8.0 syntax.

public string GetCancelOrderMessage(OrderStatus status) =>
    status switch 
    {
        OrderStatus.Pending => "Order cancelled",
        OrderStatus.Confirmed => "Please call us",
        OrderStatus.Shipped => "Cancellation failed",
        _ => "Invalid order status"
    };

2. Property Patterns

C# 8.0 further extends switch statements by allowing case conditions upon the property of an object. Let us explore this functionality by extending our example for order cancellation messages.

We have received a new requirement which states that custom orders cannot be cancelled regardless of their status. Consider the domain model class for orders:

public class Order
{
    public OrderStatus Status { get; set; }
    public bool Custom { get; set; }
}

In light of the new requirements, it’s clear that our method to determine the cancellation message for an order should take an Order object instead of just the order’s status.

C# 8.0 allows us to modify our previous code in an easy and intuitive way:

public string GetCancelOrderMessage(Order order) =>
    order switch
    {
        { Custom: true } => "Cancellation not allowed for custom order",
        { Status: OrderStatus.Pending } => "Order Cancelled",
        { Status: OrderStatus.Confirmed } => "Please call us",
        { Status: OrderStatus.Shipped } => "Cancellation not allowed for shipped order",
        _ => "Invalid Order Status"
    };

In previous versions of C#, it would not have been possible to handle this logic in a single switch statement.

3. Default Interface Implementation

C# 8.0 allows us to define a default implementation of an interface method. This can be useful if we need to add new methods to an interface without breaking existing code.

Consider an interface ITool defined in a .NET Standard 2.1 class library:

public interface ITool
{
    void PrimaryAction();
}

There is an existing .NET Core web application which references our class library and defines its own class that implements ITool:

public class Hammer : ITool
{
    public void PrimaryAction()
    {
        Debug.WriteLine("Hammered Nail");
    }
}

For the next release of our class library, we are required to add another method to ITool called SecondaryAction. However, adding a method to ITool, will break the web application’s implementation of it, that is, the Hammer class.

The solution is to provide a default implementation of SecondaryAction in the interface.

public interface ITool
{
    void PrimaryAction();
    void SecondaryAction()
    {
        Debug.WriteLine("Nothing happened");
    }
}

This way, it is not required for the Hammer class in the web application to provide an implementation for SecondaryAction.