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.