C# 8.0 Features (Part 2)
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.