(C# Language) Functional Techniques - Pattern Matching

Pattern matching is used to test an expression. In this tutorial we take various snippets to learn expression matching. One of the uses is to test if a variable matches a data type. Pattern matching is also used in switch expressions to match the arms against constants, strings or logical conditions. Pattern matching is also used for comparison of multiple inputs passed as expressions of tuple types. Lists can also be matched if you are using C# 11 or later.
(Rev. 18-Jun-2024)

Categories | About |     |  

Parveen,

Table of Contents (top down ↓)

Pattern Matching for Data Types

Pattern matching is very commonly used for testing if a variable is of certain data type. For example, we can determine if an IEnumerable is an instance of a List or of, say, an Array or, say, a Queue.

Open the program.cs file so that we can have a look at this type of pattern matching.


object o = "hello, world!";

if(o is Char)
{
  // doesn't print 
  Console.WriteLine("it is a char");
}
else if (o is string s)
{
  // prints s 
  Console.WriteLine(s);
}

Object is the ultimate base class in dotnet. Hence we can store a string in it - o = some string.

We can use is operator to test if the variable is of a Char type or String type.

We can run this code and verify that the test succeeds for a string type.

Let's take another example.

Create an int x = 5.

Then box it in an object instance o2.

We can again use an is operator to test that o2 contains an int type of data.

Video Explanation (see it happen!)

Please watch the following youtube video:

Pattern Matching in switch Expressions

Let's next take an example of a switch expression.

Open the solution explorer and add a class called MyClass to your project.

Consider this expression lambda that uses a switch expression to return a string corresponding to an integer input.


// Relational pattern 
public static String GetRating(int rating) => rating switch
{
  < 2 => "Poor",

  >= 2 and < 4 => "Good",

  _ => "Perfect"
};

The first arm uses a logical comparison of the rating identifier rating less than 2 to return a string.

The second arm uses a similar comparison with and pattern.

The default arm uses a discard underscore for unhandled conditions.

Pattern Matching with Multiple inputs

Pattern matching can also be used with tuple expressions.

Let's add another function GetQuadrant to this class.


// Positional Pattern 
public static String GetQuadrant(int x, int y) => (x, y) switch
{
  ( > 0, > 0) => "I",

  ( > 0, < 0) => "IV",

  ( < 0, > 0) => "II",

  ( < 0, < 0) => "III",

  _ => "None"
};

  

The function receives two arguments x and y. It uses a switch expression on a tuple expression formed with these arguments.

The arms of the switch use tuples to make comparisons of x and y.

If both are more than zero, then first quadrant is returned. Similar is done for others also.

Pattern Matching with Lists

In C# 11 and later we can use pattern matching with lists such as arrays.

Open the program.cs file again!

Let's create an array of three int numbers.


// list pattern matching with C# 11 or later 
int[] numbers = { 1, 2, 3 };

if (numbers is [_, var i, _])
{
  // prints 2 
  Console.WriteLine(i);
}

// source code has been attached to  
// the downloads of this tutorial course 

We can use pattern matching to test if the elements of this array match a specific pattern. For this square brackets are used with comma separators.

We can use an if condition to test if the array matches a pattern of three comma separated values. Notice that we have used discards to indicate a wild-card like match. We have used var pattern to capture the middle element in a variable called i.

I have attached the source code to the downloads of this tutorial.

Pattern Matching with CSV Strings

An interesting use of list matching is in processing CSV strings that can often be streamed from text files.

Open the MyClass.cs file again!


public static IEnumerable<String> GetCSVTransactions()
{

  yield return "22-Apr-2022, WITHDRAWAL, cheque #349, lunch, 255.73";

  yield return "05-May-2022, DEPOSIT, cash James, Hill, office , 2100.00";

}

// source code is in the downloads attached 
// to this video 

  

Let's add a function called GetCSVTransactions that returns an IEnumerable of CSV strings. This is just a simulation of a text file that contains many bank transactions.

Notice that the second string contains an additional comma due to the name of a person. Such scenarios are very common where a user input can cause unexpected irregularities in formats.

Let's see how to deal with such problems with pattern matching of C# 11.

Come to the solution explorer and set a foreach loop on the GetCSVTransactions function.


// parse comma separated data 
foreach (var entry in MyClass.GetCSVTransactions())
{
  String[] data = entry.Split(',');

  // .. dots indicates a skip of values 
  if(data is [_,String txn,..,var amt])
  {
    Console.WriteLine($"{txn} value {amt}");
  }
}

// prints  
 WITHDRAWAL value  255.73
 DEPOSIT value  2100.00

Next we split the string into an array of strings by using the split function.

After this we use an if condition to match the string against a pattern where the first value is discarded with an underscore. The second is captured in a string to indicate WITHDRAWAL or DEPOSIT. After this two dots have been used to indicate a skip of values. The last is captured in a variable called amount.

So this is how we have a possibility of using a list pattern for processing files. Thanks!


This Blog Post/Article "(C# Language) Functional Techniques - Pattern Matching" by Parveen is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.