What's pattern matching in C# 8.0?
Tags: dotnet, csharp, aspnet
Published: 2020-03-08 20:57:14

## Also in this [series](https://blog.miguelbernard.com/csharp-8-0-new-features/)

1. [C# 8.0 Nullable Reference types are here!](https://blog.miguelbernard.com/c-8-0-nullable-reference-types-are-here/)
2. [Pattern matching in C#](https://blog.miguelbernard.com/pattern-matching-in-csharp/)
3. [Asynchronous streams](https://blog.miguelbernard.com/asynchronous-streams/)
4. [Indices and ranges](https://blog.miguelbernard.com/c-8-0-indices-and-ranges)
5. [Default interface methods](https://blog.miguelbernard.com/what-are-default-interface-methods-in-c-8-0)
6. [5 tips to improve your productivity in C# 8.0](https://blog.miguelbernard.com/5-tips-to-improve-your-productivity-in-c-8-0)

> All code samples are available on [github](https://github.com/mbernard/csharp8sample)


Is it me or Functional programming (FP) seems to be trending lately? FP languages like Haskell, Elixir, and F# are stronger than ever and large frameworks try to adopt a more functional approach. Even C# tries to include functional constructs in the language.

As a matter of fact, one of the biggest addition in the latest version of C# (8.0) is pattern matching. Pattern matching is not really new in C# though. So far, it was pretty basic and felt like it was more of a second class citizen. With the latest release of C#, we now have full support for it.

# Let's dive in!
Pattern matching is achieved by using the `switch` operator. Wait a minute... doesn't `switch` already exist in the language? Yes, which makes the adoption of this new feature very confusing.

Even if the keyword, syntax, and behavior of the `switch` operator are similar, the new feature differs remarkably. One of the biggest differences is that the new `switch` is an expression and the old one is a statement. You don't have `case` and `break` statements anymore. 

## Expression vs Statement
A quick note before we continue. For most people, the difference between a statement and an expression is very unclear. Here's the best definition I've found so far.

**Expression**: Something which returns a value when evaluated
**Statement**: A line of code executing something

e.g.
*Statement*
```csharp
if(a == null) 
{ 
    someCode...
}
else 
{
    someCode2...
}
```

*Expression*
```csharp
var b = a == null ? value1 : value2;
```


## That being said
That being said, let's explore the similarities and differences between the 2 switch operators.

### Old switch
*Syntax*
```csharp
switch(value)
{
    case 1: something...
        break;
    case 2: something2...
    case 3: something3...
        break;
    default:
        somethingDefault....
        break;
}
```
The old `switch` tries to find the next instruction to branch to. Then it executes every line sequentially until it hits a `break` statement. It behaves like a `goto` statement, which is generally considered a bad practice. This operator only runs code and doesn't return anything, that's why it's considered a statement.

### New switch
*Syntax*
```csharp
var a = value1 switch 
{
    matchingExpression => expression
    matchingExpression2 => expression
    ...
}
```
On the other hand, the new `switch` must return a value. In fact, every possible match must return a value of the same type. Matching expressions can use patterns to match or not. Hence the name pattern matching.

# The power of patterns
Matching patterns not only match when a condition is true but also cast your object to the matched type and assign it to a variable automatically. From there you can match on the object properties and even evaluate boolean expressions on those properties.


## Nothing better than some examples
Let's explore a TollService application that has many business rules. We'll see how pattern matching makes the code clean and easy to read.

#### V1 - Matching on types
```csharp
public decimal CalculateToll(object vehicle)
{
    // Price per vehicle type
    // ==========
    // Car -> 2$
    // Taxi -> 3.50$
    // Bus -> 5$
    // Truck -> 10$
    return vehicle switch
    {
        Car c => 2.00m,
        Taxi t => 3.50m,
        Bus b => 5.00m,
        DeliveryTruck t => 10.00m,
        { } => throw new ArgumentException("Not a known vehicle type", nameof(vehicle)),
        null => throw new ArgumentNullException(nameof(vehicle))
    };
}
```

#### V2 - Matching on properties
You can use the `{}` to match on specific values of your object
```csharp
public decimal CalculateToll(object vehicle)
{
    // Price considering occupancy
    // ===========
    // Car and taxi
    //   No passengers -> +0.50 $
    //   2 passengers -> -0.50 $
    //   3 or more passengers -> -1$
    return vehicle switch
    {
        Car { Passengers: 0 } => 2.00m + 0.50m,
        Car { Passengers: 1 } => 2.0m,
        Car { Passengers: 2 } => 2.0m - 0.50m,
        Car _ => 2.00m - 1.0m,

        Taxi { Fares: 0 } => 3.50m + 1.00m,
        Taxi { Fares: 1 } => 3.50m,
        Taxi { Fares: 2 } => 3.50m - 0.50m,
        Taxi _ => 3.50m - 1.00m,

        Bus b => 5.00m,
        DeliveryTruck t => 10.00m,
        { } => throw new ArgumentException("Not a known vehicle type", nameof(vehicle)),
        null => throw new ArgumentNullException(nameof(vehicle))
    };
}
```

#### V3 - Conditional boolean expression
You can use the `when` keyword to define a boolean expression that will be evaluated on your object. You may already be familiar with the `when` keyword. It was previously introduced in C# 6.0 in `catch` statements to evaluate conditions on properties of the exception. As you can imagine, it works exactly the same way here, so you shouldn't be lost.
```csharp
public decimal CalculateToll(object vehicle)
{
    // Price considering occupancy
    // ===========
    // Bus
    //   Less than 50% full -> +2$
    //   More than 90% full -> -1$
    return vehicle switch
    {
        // car and taxi hidden here to make it easier to read
        // ...
        Bus b when (double)b.Riders / (double)b.Capacity < 0.50 => 5.00m + 2.00m,
        Bus b when (double)b.Riders / (double)b.Capacity > 0.90 => 5.00m - 1.00m,
        Bus _ => 5.00m,

        DeliveryTruck t => 10.00m,
        { } => throw new ArgumentException("Not a known vehicle type", nameof(vehicle)),
        null => throw new ArgumentNullException(nameof(vehicle))
    };
}
```

#### V4 - Nesting
`switch` operators must return an expression and since they are themselves expressions, it means we can nest them to make our code easier to read.
```csharp
public decimal CalculateToll(object vehicle)
{
    return vehicle switch
    {
        Car c => c.Passengers switch
        {
            0 => 2.00m + 0.5m,
            1 => 2.0m,
            2 => 2.0m - 0.5m,
            _ => 2.00m - 1.0m
        },

        Taxi t => t.Fares switch
        {
            0 => 3.50m + 1.00m,
            1 => 3.50m,
            2 => 3.50m - 0.50m,
            _ => 3.50m - 1.00m
        },
        
        // Bus and truck hidden here to make it easier to read
        // ...
    };
}
```

#### Special match expressions
You may have noticed in the previous examples that we've been using some special matching expressions, `{}`, `null` and `_`.

- `{}`: Non-null, but an unknown type
- `null`: null value
- `_`: Wildcard that matches absolutely anything but doesn't capture the value in a new variable

#### Gotcha
Matching expressions are evaluated from top to bottom. Always put the most restrictive matching expression first and try to finish with the 'special' match expressions.
e.g. If you use `_` as the first line, it will always match on this expression and nothing else will be evaluated.

# Closing word
Pattern matching is super powerful and can be used to simplify complex business logic while making it easier to read. Stay tuned for more blog posts in this series!


## Also in this [series](https://blog.miguelbernard.com/csharp-8-0-new-features/)

1. [C# 8.0 Nullable Reference types are here!](https://blog.miguelbernard.com/c-8-0-nullable-reference-types-are-here/)
2. [Pattern matching in C#](https://blog.miguelbernard.com/pattern-matching-in-csharp/)
3. [Asynchronous streams](https://blog.miguelbernard.com/asynchronous-streams/)
4. [Indices and ranges](https://blog.miguelbernard.com/c-8-0-indices-and-ranges)
5. [Default interface methods](https://blog.miguelbernard.com/what-are-default-interface-methods-in-c-8-0)
6. [5 tips to improve your productivity in C# 8.0](https://blog.miguelbernard.com/5-tips-to-improve-your-productivity-in-c-8-0)

> All code samples are available on [github](https://github.com/mbernard/csharp8sample)

# References
https://docs.microsoft.com/en-us/dotnet/csharp/tutorials/pattern-matching