Tuple Pattern Matching In C#

by Kenji Elzerman
Tuple Pattern Matching In C# - Kens Learning Curve

When C# 7.0 was released I noticed there was something called pattern matching. Pattern matching is taking an expression and testing whether it matches certain criteria. A criterion could be ‘is of type’. There are different types of pattern matching, but since C# 8.0 we have a new one: Tuple pattern matching in C#. In this article, I want to explain what it is and how to use it by using simple examples.

Tuples In General

Before we dive into the tuple pattern matching in C#, let’s take a look at tuples, or ValueTuple if you want to know the C# data type.

Introduced in 2017 with C# 7.0 (.NET Framework 4.7), ValueTuple provides a very convenient and easy way to handle multiple values. It has gained a lot of popularity ever since. The idea behind a ValueTuple is pretty simple: It’s a container that can hold multiple values without needing to create a class with properties.

Here is a basic example of a tuple:

(string Title, int Rating) movie = (Title: "The Matrix", Rating: 5);

Console.WriteLine(movie);

I have declared a variable movie, which is of the type (string Title, int Rating). This is a bit unusual because normally we have just one data type and not two. When I run the application I see a result like this:

valuetuple result - C# ValueTuple - Kens Learning Curve

To get only the title or only the rating, you use the following code:

(string Title, int Rating) movie = (Title: "The Matrix", Rating: 5);

Console.WriteLine($"This movie is {movie.Title} and has a rating of {movie.Rating}");

The variable movie is somewhat a class with properties. You can also create a method that returns a ValueTuple:

var foundMovie = GetMovie();
Console.Write($"The movie {foundMovie.Title} has a rating of {foundMovie.Rating}");

public (string Title, int Rating) GetMovie()
{
    return (Title: "The Matrix", Rating: 5);
}

And that’s tuples in a nutshell. If you want to know more about tuples, please check out the article How to use ValueTuple in C#.

Project Preparation

I will use examples in the next chapter. These examples are based on code that I already have. You can recreate the same situation if you want.

For this article, I will be using a console application. Inside this application, I am only going to change the Program.cs, to keep it simple. There is one class and one enum:

public class Product
{
    public int Id { get; set; }
    public string Title { get; set; }
    public int Stock { get; set; }
    public Status Status { get; set; }
    public bool Available { get; set; }
}

public enum Status
{
    Ordered,
    Delivered,
    Delayed,
    Unknown
}

I think this speaks for itself; there is a class Product that has certain properties. One of the properties is of the type Status, which is an enum, located under the class Product.

Next, I will create a list of products and place it above the class Product:

List<Product> products = new()
{
    new() { Id = 1, Title = "7Up", Status = Status.Ordered, Stock = 10, Available = true },
    new() { Id = 2, Title = "Chips", Status = Status.Ordered, Stock = 0, Available = true },
    new() { Id = 3, Title = "Sugar", Status = Status.Delivered, Stock = 67, Available = true },
    new() { Id = 4, Title = "Meatballs", Status = Status.Delivered, Stock = 5, Available = true },
    new() { Id = 5, Title = "Milk", Status = Status.Delivered, Stock = 7, Available = true },
    new() { Id = 6, Title = "Chewinggum", Status = Status.Delivered, Stock = 0, Available = false },
    new() { Id = 7, Title = "Toiletpaper", Status = Status.Delivered, Stock = 1, Available = true },
    new() { Id = 8, Title = "Tea", Status = Status.Delivered, Stock = 5, Available = true },
    new() { Id = 9, Title = "Coffee", Status = Status.Delivered, Stock = 85, Available = true },
    new() { Id = 10, Title = "Biscuits", Status = Status.Delivered, Stock = 12, Available = true },
    new() { Id = 11, Title = "Chocolate", Status = Status.Delivered, Stock = 89, Available = true },
    new() { Id = 12, Title = "Bread", Status = Status.Delivered, Stock = 167, Available = true },
};

The Use Case

Writing if-statements with checking more than one value can be a bit overkill, especially with the more complex types.

For example: You want to check if the availability of a product is true or false and what the status of the order is. You could write an if-statement like this:

Product product = products[2];
if (product.Available && product.Status == Status.Delivered)
{
    Console.WriteLine($"The {product.Title} has been delivered, but there still is stock.");
}
else if (!product.Available && product.Status == Status.Delivered)
{
    Console.WriteLine($"The {product.Title} has been delivered, meaning there is stock again.");
}
else if (product.Available && product.Status == Status.Delayed)
{
    Console.WriteLine($"Order of {product.Title} has been delayed, but there is stock.");
}
else if (!product.Available && product.Status == Status.Delayed)
{
    Console.WriteLine($"Order of {product.Title} has been delayed and there is no stock.");
}
else if (product.Available && product.Status == Status.Ordered)
{
    Console.WriteLine($"{product.Title} has been ordered, but is still available.");
}
else if (!product.Available && product.Status == Status.Delivered)
{
    Console.WriteLine($"The {product.Title} has been delivered, meaning there is stock again.");
}
else if (!product.Available && product.Status == Status.Ordered)
{
    Console.WriteLine($"{product.Title} is not in stock, but it has been ordered.");
}

I think we can all agree this isn’t very good code. Well, it works, but it’s not easy to read and hard(er) to expand if needed.

Switching To Tuple Pattern Matching In C#

The use case code example is a bit much and we can make it shorter with tuple pattern matching in C#. 

The Pattern

If we refactor the if-statement to the tuple pattern matching in C# it looks like this:

Console.WriteLine(GetShortDescription(products[2]));

string GetShortDescription(Product product) => (product.Available, product.Status) switch
{
    (true, Status.Ordered) => $"{product.Title} has been ordered, but is still available.",
    (false, Status.Delivered) => $"The {product.Title} has been delivered, meaning there is stock again.",
    (false, Status.Ordered) => $"{product.Title} is not in stock, but it has been ordered.",
    (false, Status.Delayed) => $"Order of {product.Title} has been delayed and there is no stock.",
    (true, Status.Delayed) => $"Order of {product.Title} has been delayed, but there is stock.",
    (false, Status.Delivered) => $"The {product.Title} has been delivered, meaning there is stock again.",
    (true, Status.Delivered) => $"The {product.Title} has been delivered, but there still is stock.",
};

This is a switch with two parameters: the availability and the status of the product. Each case checks the availability and the status. If both are true, the string is returned. Of course, you can return other types, if needed.

Although I don’t think it’s better for reading, it is a little bit easier to follow and it uses less code. Expanding this switch is also easier and you can position each case as you please.

The Default

Just like a switch, the tuple pattern matching in C# has a default case too. But instead of using the keyword default, you will use the discards character. This is an underscore that creates a dummy variable.

Console.WriteLine(GetShortDescription(products[2]));

string GetShortDescription(Product product) => (product.Available, product.Status) switch
{
    (true, Status.Ordered) => $"{product.Title} has been ordered, but is still available.",
    (false, Status.Delivered) => $"The {product.Title} has been delivered, meaning there is stock again.",
    (false, Status.Ordered) => $"{product.Title} is not in stock, but it has been ordered.",
    (false, Status.Delayed) => $"Order of {product.Title} has been delayed and there is no stock.",
    (true, Status.Delayed) => $"Order of {product.Title} has been delayed, but there is stock.",
    (true, Status.Delivered) => $"The {product.Title} has been delivered, but there still is stock.",
    _ => $"No product found that matches the criteria"
};

If statements VS Tuple Pattern Matching In C#

The question is: Which one is better? Well, both are good. But there are some differences between both ways.

Unreachable

This is one really big advantage to the tuple pattern matching in C#. It will check if you have unreachable code. If you paste the example code into a C# project you will notice that it gives an error:

Error in code - Tuple Pattern Matching With C# - Kens Learning Curve

This is because that case has already been made. Look at the second case. But the same ‘mistake’ is in the if-statement in the earlier example. But you won’t see it, because there is no error or warning on if-statements.

Benchmark

I also benchmarked the difference between the if-statements and the tuple pattern matching in C#. This is the result:

The tuple pattern matching in C# is slightly faster, with less error too.

Conclusion On Tuple Pattern Matching In C#

I believe the tuple pattern matching in C# could work in a lot of scenarios. Although, it’s not easy to read and follow if you are not used to them. But big if-statements aren’t convenient either.

You could use the strategy pattern if you have big code bodies for the if-statements rather than switch to tuple pattern matching. This would make the code even better. But if you have just a single line of code to execute, a strategy pattern would be a waste of resources.

In the end, it all comes down to what you need, what you do, and where you want to go. But I strongly believe the tuple pattern matching in C# is a really good addon.

Test Your Knowledge

MAINTENANCE MODE

Related Articles

Table Of Contents
Kens Learning Curve