Pattern matching in C#

Pattern matching was introduce in C# 7.0. But, you don’t remember.

Pattern matching is a feature that allows you to implement method dispatch on properties other than the type of an object.

Now, with the upcoming release of C# 8.0 pattern matching have a few awesome improvements.

But first let’s see what we can do with the current version.

  • case 0: is the familiar constant pattern.
  • case IEnumerable<int> childSequence: is a type pattern.
  • case int n when n > 0: is a type pattern with an additional when condition.
  • case null: is the null pattern.
  • default: is the familiar default case

To show you an example of all of the above

public static double ComputeArea(object shape)
{
    switch (shape)
    {
        case Square s when s.Side == 0:
        case Circle c when c.Radius == 0:
        case Triangle t when t.Base == 0 || t.Height == 0:
        case Rectangle r when r.Length == 0 || r.Height == 0:
            return 0;

        case Square s:
            return s.Side * s.Side;
        case Circle c:
            return c.Radius * c.Radius * Math.PI;
        case Triangle t:
            return t.Base * t.Height / 2;
        case Rectangle r:
            return r.Length * r.Height;
        case null:
            throw new ArgumentNullException(paramName: nameof(shape), message: "Shape must not be null");
        default:
            throw new ArgumentException(
                message: "shape is not a recognized shape",
                paramName: nameof(shape));
    }
}

As you see in the above example Pattern Matching help us to have a more readable and extended way to use Switch statement specially when we have extra conditions.But we can also use Pattern Matching in if statements.

public static double ComputeAreaModernIs(object shape)
{
    if (shape is Square s)
        return s.Side * s.Side;
    else if (shape is Circle c)
        return c.Radius * c.Radius * Math.PI;
    else if (shape is Rectangle r)
        return r.Height * r.Length;
    // elided
    throw new ArgumentException(
        message: "shape is not a recognized shape",
        paramName: nameof(shape));
}

C# 8.0 introduce more pattern expressions in more places to use. Consider these features when your data and functionality are separate. Some of the new places are properties, using statement, the lambda symbol usage, and Tuples. Yes! now we can use switch statements for Tuples. Let’s jump into the syntax.

public static RGBColor FromRainbow(Rainbow colorBand) =>
    colorBand switch
    {
        Rainbow.Red    => new RGBColor(0xFF, 0x00, 0x00),
        Rainbow.Orange => new RGBColor(0xFF, 0x7F, 0x00),
        Rainbow.Yellow => new RGBColor(0xFF, 0xFF, 0x00),
        Rainbow.Green  => new RGBColor(0x00, 0xFF, 0x00),
        Rainbow.Blue   => new RGBColor(0x00, 0x00, 0xFF),
        Rainbow.Indigo => new RGBColor(0x4B, 0x00, 0x82),
        Rainbow.Violet => new RGBColor(0x94, 0x00, 0xD3),
        _              => throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand)),
    };

Doesn’t that look bad-ass! notice how many improvement for the switch statement no more : or break; and the default case is replace by a _

public static decimal ComputeSalesTax(Address location, decimal salePrice) =>
    location switch
    {
        { State: "WA" } => salePrice * 0.06M,
        { State: "MN" } => salePrice * 0.75M,
        { State: "MI" } => salePrice * 0.05M,
        _ => 0M
    };

The property pattern enables you to match on properties of the object examined.

Tuples Pattern Matching is a awesome improvement that C# 8.0 brings. Let’s take a look.

public static string RockPaperScissors(string first, string second)
    => (first, second) switch
    {
        ("rock", "paper") => "rock is covered by paper. Paper wins.",
        ("rock", "scissors") => "rock breaks scissors. Rock wins.",
        ("paper", "rock") => "paper covers rock. Paper wins.",
        ("paper", "scissors") => "paper is cut by scissors. Scissors wins.",
        ("scissors", "rock") => "scissors is broken by rock. Rock wins.",
        ("scissors", "paper") => "scissors cuts paper. Scissors wins.",
        (_, _) => "tie"
    };

There you have, Pattern Matching in C# is improves well extends the usage of switch statement to a whole new level. We are no longer constraint the old timer switch statement where we have to have a exact match. Now, we can virtually query with within the case using the keyword when or we can extend to property or Tuples. This is a game changing update for C#.

References


https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8#switch-expressions

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

Spread the word
  • Yum