.NET, C#, Programming

C# 8.0 pattern matching in action

Let’s revisit definition of the pattern matching from the Wiki:

In computer sciencepattern matching is the act of checking a given sequence of tokens for the presence of the constituents of some pattern. In contrast to pattern recognition, the match usually has to be exact: “either it will or will not be a match.” The patterns generally have the form of either sequences or tree structures. Uses of pattern matching include outputting the locations (if any) of a pattern within a token sequence, to output some component of the matched pattern, and to substitute the matching pattern with some other token sequence

Pattern matching

Putting it into the human language means that instead of comparing expressions by exact values (think of if/else/switch statements), you literally match by patterns or shape of the data. Pattern could be constant value, tuple, variable, etc. For full definition please refer to the documentation. Initially, pattern matching was introduced in C# 7. It was shipped with basic capabilities to recognize const, type and var patterns. The language was extended with is and when keywords. One of the last pieces to make it work was introduction of discards for deconstruction of tuples and objects. Combining all these together you was able to use pattern matching in if and switch expressions:

if (o is null) Console.WriteLine("o is null");
if (o is string s && s.Trim() != string.Empty)
        Console.WriteLine("whoah, o is not null");
switch (o)
{
    case double n when n == 0.0: return 42;
    case string s when s == string.Empty: return 42;
    case int n when n == 0: return 42;
    case bool _: return 42;
    default: return -1;
}

C# 8 extended pattern matching with switch expressions and three new ways of expressing a pattern: positional, property and tuple. Again, I will refer to full documentation for the details.

In this post I would like to show a real-world example of using pattern matching power. Let’s say you want to build an SQL-query based on list of filters you receive as an input from HTTP request. For example we would like to get a list of all shurikens filtered by shape and material:

http://www.ninja.org/shuriken?filters[shape]='star,stick'&filters[material]='kugi-gata'

We need a model to which we can map this request with list of filters:

public class ShurikenQuery
{
    [BindProperty(Name = "filters", SupportsGet = true)]
    public IDictionary<string, string> ShurikenFilters { get; set; }
}

Now, let’s write a function which builds SQL-query string based on provided filters:

private static string BuildFilterExpression(ShurikenQuery query)
{
    if (query is null)
        throw new ArgumentNullException(nameof(query));

    const char Delimiter = ',';

    var expression = query.ShurikenFilters?.Aggregate(new StringBuilder(), (acc, ms) =>
    {
        var key = ms.Key;
        var value = ms.Value;
        var exp = (key, value) switch
        {
            (_, null) => $"[{key}] = ''",
            (_, var val) when val.Contains(Delimiter) =>
                @$"[{key}] IN ({string.Join(',', val
                    .Replace("'", string.Empty)
                    .Replace("\"", string.Empty)
                    .Split(Delimiter).Select(x => $"'{x}'"))})",
            (_, _) => $"[{key}] = '{value}'"
        };
        return exp != null ? acc.AppendLine($" AND {exp}") : acc;
    });
    return expression?.ToString() ?? string.Empty;
}

Let’s break this code down. On line 8 we use LINQ Aggregate function which do the main work of building a filter string. We want to iterate over each KeyValuePair in dictionary and based on the data shape in it create a string which represents expression which could be provided for SQL WHERE clause.

On line 10 and 11 we extract key and value for KeyValuePair in own variables just for convenience.

Lines between 12-21 are of main interest to us – that’s where all magic happens. On line 12 we wrapped key and value in a tuple and use switch expression to start pattern match. On line 14 we check if value variable is null (_ in key position means discard – it’s when we don’t care what actual value is). If that’s a case we produce string like [Shape]=”. On line 15-19 again, we not interested what in key position, but now we assign value to a dedicated variable val we can work with. Next, we check if this value contains filter with multiple values (like in case filters[shape]=’star,stick’) and split it in separate values, removing ” and ‘ on the way we go. We want to translate this into SQL IN operator, so string after processing this pattern looks like this: [Shape] IN (‘star’, ‘stick’). Last pattern matches for remaining cases of single value filters (like filters[material]=’kugi-gata’) which produce following string: [Material] = ‘kugi-gata’. Line 23 applies AND to string we built and accumulating result in a StringBuilder variable we provided in initial run of Aggregate function on line 8.

If we would put (_, _) as a first line in a switch expression other patterns will be not evaluated, because (_, _) will catch all values

Be aware that in pattern matching the order is everything

Finally, the resulting string we return looks like this:

 AND [Shape] IN ('star','stick') AND [Material] = 'kugi-gata'

And that’s a valid string for SQL WHERE clause. As you can see pattern matching is a real deal and could be used in a lot of cases for parsing expressions. With C# 9 pattern matching will be extended even more with relational and logical patterns.

Hope you enjoyed. Stay tuned and happy coding.

.NET, C#, Programming

Make your C# code cleaner with functional approach

Since introducing LINQ in .NET 3.5, the way how we write code changed a lot. Not only in the context of database queries with LINQ to SQL or LINQ to Entities, but also in day-to-day work with manipulating collections and all kind of transformations. Powerful language constructs like implicitly typed variables, anonymous types, lambda expressions and object initializers, gave us tools for writing more robust and conciseness code.

It was a big step towards functional approach to solve engineering tasks by using a more declarative way of expressing your intent instead of sequential statements in imperative paradigm.

Functional programming is a huge topic and mind shift for all .NET developers who is writing their code in C# for a long time. If you are new to the topic (like me), you probably don’t want to get into all that scary sounding things like functors, applicatives or monands right now (discussion for other posts). So let’s see how applying a functional approach could make your code cleaner here and now with our beloved C#.

For the sake of example we will solve a very simple FizzBuzz kata in C#. I will show you how it looks like in F#. If you don’t know what is kata, it just a fancy way of saying puzzle or coding task. The word kata came to us from the world of martial arts and particularly Karate. The FizzBuzz is a simple coding task where you need to solve the following problem:

Write a program that prints the numbers from 1 to 100. But for multiples of three print “Fizz” instead of the number and for the multiples of five print “Buzz”. For numbers which are multiples of both three and five print “FizzBuzz “

So, first we will start with naïve implementation in C#:

void Main()
{
    for(var i = 1; i <= 100; i++)
    {
        if(i % (3 * 5) == 0)
            Console.WriteLine("FizzBuzz");
        else if(i % 3 == 0)
            Console.WriteLine("Fizz");
        else if(i % 5 == 0)
            Console.WriteLine("Buzz");
        else
            Console.WriteLine(i);
    }   
}

And here’s the output:

1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
...

So far so good. I told you, it’s a piece of a cake. Okay, how we can improve this code? Let’s use the power and beauty of LINQ:

void Main()
{
	var result = Enumerable
		.Range(1, 100)
		.Select(x => {
			switch(x)
			{
				case var n when n % (3 * 5) == 0: return "FizzBuzz";
				case var n when n % 3 == 0: return "Fizz";
				case var n when n % 5 == 0: return "Buzz";
				default: return x.ToString();
			}			
		})
		.Aggregate((x, y) => x + Environment.NewLine + y);
	
	Console.WriteLine(result);
}

We use the static helper Range on Enumerable to generate a sequence from 1 to 100. Then we use the Select method to iterate over each number in that range and return a string which contains one of those FizzBuzz words. We used very powerful concept – pattern matching. This feature is available from C# 7.0. This variation of pattern matching uses var pattern with when clause for specifying condition. Last method in chain is Aggregate. It is one of the most interesting in the LINQ – you could use it as a functional replacement for the loops in your code base. In this example we concatenated each element in sequence with a new line producing string as a result.

In C# 8.0 pattern matching was extended and improved. We can re-write our code like this:

public static string FizzBuzz(int n) =>
        (n % 3, n % 5) switch
        {
            (0, 0) => "FizzBuzz",
            (0, _) => "Fizz",
            (_, 0) => "Buzz",
            (_, _) => $"{n}"
        };
 
 static void Main(string[] args)
 { 
     foreach (var n in Enumerable.Range(1, 100))
     {
         Console.WriteLine(FizzBuzz(n));
     }
 }

This syntax is much closer to how pattern matching is applied in functional languages. In functional languages _ is called a discard symbol – meaning we are not interested in value in that position. We used what is called tuple pattern here.

  • When remainder of 3 and 5 in both positions 0 – we print “FizzBuzz”.
  • When remainder of 3 is 0 and we not interested in the remainder of 5, we print “Fizz”.
  • When the remainder of 5 is 0 and we not interested in the remainder of 3 we print “Buzz”.
  • For all other cases we just print value of the n.

Remember, in pattern matching order matters – first matched condition win and further calculation stops.

Finally, let’s look at the F# implementation of the kata:

let fizzBuzz list  = 
    list |> List.map (fun x -> 
        match (x % 3, x % 5) with
        | (0, 0) -> "FizzBuzz"
        | (0, _) -> "Fizz"
        | (_, 0) -> "Buzz"
        | _ -> string x
    )

fizzBuzz [1..100] |> List.iter (fun x -> printfn "%s" x)

You can see that this sample is very similar to the previous one with C# 8.0 pattern matching. And this should not surprise you, because the C# team is introducing more and more functional constructs in the language with each version, taking all the good parts from F#.