Pattern matching is another idea common to functional languages that has gradually crept into C#. Pattern matching refers to extracting information from structured data by matching the shape of that data.

We’ve already seen the pattern-matching is operator in our discussion of casting. This allows us to extract the cast version of a variable and assign it to a new one:

if(oldVariable is SpecificType newVariable)
{
    // within this block newVariable is (SpecificType)oldVariable
}

The switch statement is also an example of pattern matching. The traditional version only matched constant values, i.e.:

switch(choice)
{
    case "1":
        // Do something
        break;
    case "2":
        // Do something else
        break;
    case "3":
        // Do a third thing
        break;
    default:
        // Do a default action
        break;
}

However, in C# version 7.0, this has been expanded to also match patterns. For example, given a Square, Circle, and Rectangle class that all extend a Shape class, we can write a method to find the area using a switch:

public static double ComputeCircumference(Shape shape)
{
    switch(shape)
    {
        case Square s:
            return 4 * s.Side;
        case Circle c:
            return c.Radius * 2 * Math.PI;
        case Rectangle r:
            return 2 * r.Length + 2 * r.Height;
        default:
            throw new ArgumentException(
                message: "shape is not a recognized shape",
                paramName: nameof(shape)
            );
    }
}

Note that here we match the type of the shape and cast it to that type making it available in the provided variable, i.e. case Square s: matches if shape can be cast to a Square, and s is the result of that cast operation.

This is further expanded upon with the use of when clauses, i.e. we could add a special case for a circle or square with a circumference of 0:

public static double ComputeCircumference(Shape shape)
{
    switch(shape)
    {
        case Square s when s.Side == 0:
        case Circle c when c.Radius == 0:
            return 0;
        case Square s:
            return 4 * s.Side;
        case Circle c:
            return c.Radius * 2 * Math.PI;
        case Rectangle r:
            return 2 * r.Length + 2 * r.Height;
        default:
            throw new ArgumentException(
                message: "shape is not a recognized shape",
                paramName: nameof(shape)
            );
    }
}

The when applies conditions to the match that only allow a match when the corresponding condition is true.

Info

C# 8.0, which is currently in preview, has expanded greatly upon pattern matching, adding exciting new features, such as the switch expression, tuples, and deconstruction operator.