joydip_kanjilal
Contributor

How to use pattern matching in C#

how-to
Mar 15, 20216 mins
C#Development Libraries and FrameworksMicrosoft .NET

Take advantage of the new relational and logical patterns in C# 9.0 to make your code more readable, maintainable, and efficient.

mismatched shoes
Credit: Thinkstock

Pattern matching is a great feature first introduced in C# 7. You can use pattern matching on any data type, even on custom data types, to extract values from an expression.

Pattern matching was enhanced in C# 8, which introduced a wide array of new pattern types. It was enhanced even further in C# 9, with the addition of new relational and logical patterns. This article talks about the newly added patterns in C# 9 and how we can take advantage of them, with code examples wherever appropriate.

To work with the code examples provided in this article, you should have Visual Studio 2019 installed in your system. If you don’t already have a copy, you can download Visual Studio 2019 here.

Create a console application project in Visual Studio

First off, let’s create a .NET Core console application project in Visual Studio. Assuming Visual Studio 2019 is installed in your system, follow the steps outlined below to create a new .NET Core console application project in Visual Studio.

  1. Launch the Visual Studio IDE.
  2. Click on “Create new project.”
  3. In the “Create new project” window, select “Console App (.NET Core)” from the list of templates displayed.
  4. Click Next.
  5. In the “Configure your new project” window, specify the name and location for the new project.
  6. Click Create.

We’ll use this project to explore the new pattern matching features in C# 9 in the subsequent sections of this article.

Relational patterns in C# 9

Relational patterns enable the use of the relational operators less than <, less than or equal to <=, greater than >, and greater than or equal to >=.

Consider the following class named Employee.

public class Employee
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public double Basic { get; set; }
        public double Tax { get; set; }
        public double NetSalary { get; set; }
    }

The following code snippet illustrates how you can take advantage of relational patterns to calculate the tax rate for an employee.

private static double GetTaxPercentage(Employee employee) => employee.Basic switch
{
   <= 1000 => 5.00,
   <= 5000 => 10.00,
   <= 10000 => 20.00,
   _ => 30.00
};

The following code snippet shows how you can create an instance of the Employee class, initialize its data members, and display the percentage of tax (calculated using the code snippet shown above) at the console.

static void Main(string[] args)
{
   Employee employee = new Employee { Id = 1, FirstName = "Joydip", LastName = "Kanjilal", Basic = 8000 };
   Console.WriteLine("The percentage of income tax is: " + GetTaxPercentage(employee));
   Console.Read();
}

When you execute the above program, the following output will display in the console window:

The percentage of income tax is: 20

Logical patterns in C# 9

Logical patterns enable you to use the logical operators and, or, and not. Note that you can use logical patterns and relational patterns independently or in combination.

The addition of conjunctive, disjunctive, and negation patterns makes pattern matching in C# much more flexible. While the and keyword requires the patterns on both sides of the conjunction to match, the or keyword requires only one of the patterns to match. Naturally, the not keyword is used to represent negation and requires that the pattern does not match the specified condition.

The following code snippet — a rewrite of the GetTaxPercentage method — illustrates how logical patterns can be combined with relational patterns in C# 9.

private static double GetTaxPercentage(Employee employee) => employee.Basic switch
{
   <= 1000 => 5.00,
   > 1000 and <= 5000 => 10.00,
   > 5000 and <= 10000 => 20.00,
   _ => 30.00
};

Negation patterns

The not pattern is yet another pattern that takes advantage of the not logical operator and can be used in an if construct or even with a ternary statement.

Consider the following class named RetiredEmployee that extends the Employee class and adds two data members.

public class RetiredEmployee: Employee
{
   public bool IsRetired { get; set; }
   public double Pension { get; set; }
}

The following code snippet illustrates how the negation pattern can be used.

private static bool IsRetired(Employee employee)
{
   if (employee is not RetiredEmployee)
      return false;
   return true;
}

You can call the above method from the Main function as shown below:

static void Main(string[] args)
{
    Employee employee = new RetiredEmployee { Id = 1, FirstName = "Joydip",
    LastName = "Kanjilal", IsRetired = true, Pension = 10000 };
    Console.WriteLine(IsRetired(employee)? "Retired...":"Not retired...");
    Console.Read();
}

When you execute the above program, you should see the following output in the console window:

Retired...

Conjunctive and disjunctive patterns

The conjunctive and disjunctive patterns introduced in C# 9 are so named because they rely on the logical operators for conjunction and disjunction, and and or. While and requires both patterns to match, or requires just one of the patterns to match.

The following code snippet illustrates how you can use and logic on two different patterns.

if (employee.Tax is double and >= 10)
{
   // This code block will be executed if 
   // Tax property is of type double and 
   // the value of tax is 10 or greater
   Console.WriteLine("The monthly income tax: 10%");
}

Note that the preceding code snippet combines the conjunctive and relational patterns and combines comparisons of types and properties.

And the following code snippet shows how the disjunctive or can be used to execute logic on either of two different matches.

if (employee.Tax is (10 or 20) and int tax)
{
   // This code block will be executed if the value of tax is 10 or 20  
}

Pattern matching can help you write code that is more readable, maintainable, and efficient. Because pattern matching makes it easier to traverse complex object structures, you will find it quite useful to include in the if and switch statements in your code. C# 9.0 introduces several enhancements to pattern matching over its earlier incarnations.

Finally, it should be noted that pattern matching works with constants only. If you try to use a variable or an object in lieu of a constant in a pattern matching expression, you will be greeted with the following error:

joydip_kanjilal
Contributor

Joydip Kanjilal is a Microsoft Most Valuable Professional (MVP) in ASP.NET, as well as a speaker and the author of several books and articles. He received the prestigious MVP award for 2007, 2008, 2009, 2010, 2011, and 2012.

He has more than 20 years of experience in IT, with more than 16 years in Microsoft .Net and related technologies. He has been selected as MSDN Featured Developer of the Fortnight (MSDN) and as Community Credit Winner several times.

He is the author of eight books and more than 500 articles. Many of his articles have been featured at Microsoft’s Official Site on ASP.Net.

He was a speaker at the Spark IT 2010 event and at the Dr. Dobb’s Conference 2014 in Bangalore. He has also worked as a judge for the Jolt Awards at Dr. Dobb's Journal. He is a regular speaker at the SSWUG Virtual Conference, which is held twice each year.

More from this author