Overview of scenarios where you can use pattern matching. These techniques can improve the readability and correctness of your code.
Reduce nested if-else
I have a complex nested if-else statements scenario
// DON'T DO THIS
if (order.Status == "Pending")
{
ProcessPendingOrder(order);
} else if (order.Status == "Completed")
{
ProcessCompletedOrder(order);
} else if (order.Status == "Cancelled")
{
ProcessCancelledOrder(order);
} else
{
throw new InvalidOperationException("Unknown status");
}
instead do this
order.Status switch
{
"Pending" => ProcessPendingOrder(order),
"Completed" => ProcessCompletedOrder(order),
"Cancelled" => ProcessCancelledOrder(order),
_ => throw new InvalidOperationException("Unknown status")
};
Using it as a method
You can test a variables to find a match on specific values
public State PerformOperation(string command) =>
command switch
{
"SystemTest" => RunDiagnostics(),
"Start" => StartSystem(),
"Stop" => StopSystem(),
_ => throw new ArgumentException("Invalid string value", nameof(command)),
};
_
is the discard pattern that matches all values. It handles any error conditions where the value doesn’t match one of the defined values.
Another example
We have the following class
IAnimal animal = new Cat("Fred", 12);
then with a single if
we can determine if the animal is more than 10 years old and has the name of fred
if(animal is Cat { CatYears: >10, Name: "Fred"} myCat)
{
Console.WriteLine($"{myCat.CatYears} old");
}
Null checks
One of the most common scenarios.
string? message = MaybeString();
if(message is not null)
{
Console.WriteLine(message);
}
Relational patterns
You can use relational patterns to test how a value compares to constants.
string WaterState(int tempInFahrenheit) =>
tempInFahrenheit switch
{
(>32) and (<212) => "liquid",
<= 32 => "solid",
>= 212 => "gas"
};
Multiple inputs
You can write patterns that examine multiple properties of an object.
consider the following Order record
public record Order(int Items, decimal Cost);
this examines the number of items and the value of an order to calculate a discount
public decimal CalculateDiscount(Order order) =>
order switch
{
{ Items: >10, Cost: >1000.00m } => 0.10m,
{ Items: >5, Cost: > 500.00m } => 0.05m,
{ Cost: >250.00m } => 0.02m,
null => throw new ArgumentNullException(nameof(order), "can't calculate on null"),
var someObject => 0m,
};
List patterns
Since c# 11 we may check for list emptiness
check for list emptiness
if (items is [])
throw new ArgumentException("Input must not be empty");
if (items is not [])
Console.WriteLine("list is not empty");
Reference(s)
https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/pattern-matching
https://ianvink.wordpress.com/2021/12/11/part-1-c-10-pattern-matching-or-the-death-of-if-else/
https://dev.to/ahmedshahjr/improve-your-c-code-with-pattern-matching-5g7c
https://endjin.com/blog/2023/03/dotnet-csharp-11-pattern-matching-lists