Take advantage of the new relational and logical patterns in C# 9.0 to make your code more readable, maintainable, and efficient. 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. Launch the Visual Studio IDE. Click on “Create new project.” In the “Create new project” window, select “Console App (.NET Core)” from the list of templates displayed. Click Next. In the “Configure your new project” window, specify the name and location for the new project. 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: // Compiler error CS0150: A constant value is expected How to do more in C#: How to work with read-only collections in C# How to work with static anonymous functions in C# 9 How to work with record types in C# How to use implicit and explicit operators in C# Singleton vs. static classes in C# How to log data to the Windows Event Log in C# How to use ArrayPool and MemoryPool in C# How to use the Buffer class in C# How to use HashSet in C# How to use named and optional parameters in C# How to benchmark C# code using BenchmarkDotNet How to use fluent interfaces and method chaining in C# Related content news Microsoft unveils imaging APIs for Windows Copilot Runtime Generative AI-backed APIs will allow developers to build image super resolution, image segmentation, object erase, and OCR capabilities into Windows applications. By Paul Krill Nov 19, 2024 2 mins Generative AI APIs Development Libraries and Frameworks news Akka distributed computing platform adds Java SDK Akka enables development of applications that are primarily event-driven, deployable on Akka’s serverless platform or on AWS, Azure, or GCP cloud instances. By Paul Krill Nov 18, 2024 2 mins Java Scala Serverless Computing news Spin 3.0 supports polyglot development using Wasm components Fermyon’s open source framework for building server-side WebAssembly apps allows developers to compose apps from components created with different languages. By Paul Krill Nov 18, 2024 2 mins Microservices Serverless Computing Development Libraries and Frameworks how-to How to use DispatchProxy for AOP in .NET Core Take advantage of the DispatchProxy class in C# to implement aspect-oriented programming by creating proxies that dynamically intercept method calls. By Joydip Kanjilal Nov 14, 2024 7 mins Microsoft .NET C# Development Libraries and Frameworks Resources Videos