C# v7.0 pattern matching

Visual Studio “15” Preview 4 was recently released, so I decided to take it for a spin. In the upcoming C# 7.0, of the features being implemented, pattern matching is probably the most interesting. Consider how often we’ve designed code that uses a switch statement with complex cases, but then when we go to implement the design, a nested if-then-else statement must be coded instead because switch labels must be constant expressions. With pattern matching, this will finally change.

In theory, when the feature is fully implemented, we could write complex switch statements such as:

namespace ConsoleApplication4
{
    class Program
    {
        static void Main(string[] args)
        {
            for (int j = 0; j < 20; ++j)
            {
                System.Console.WriteLine("j = " + j);
                switch (j)
                {
                    case var i when i % 2 == 0:
                        System.Console.WriteLine("Even");
                        break;
                    case var i when i > 10:
                        System.Console.WriteLine("OK, > 10");
                        break;
                    default:
                        System.Console.WriteLine("Anything else");
                        break;
                }
            }
        }
    }
}

Unfortunately, while this example compiles, it does not run properly yet, in Visual Studio “15” Preview 4.

Years ago, I used to write C code that would contain declaration expressions. This would simultaneously assign a value and test to see if the value was non-zero. You can do this in C++, e.g.,

// ConsoleApplication2.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

int foo() { return 1; }

int main()
{
    if (int a = foo()) printf("a = %d\n", a);

	// if ((int b = foo())) printf("\n"); does not compile.

	if (int x = 1 == 2)
		printf("x = %d\n", x); // OK, but x is 0, not 1 because of precedence rules.
	else
		printf("x = %d\n", x);
	
    // if ((int y = 1) == 2) return y; does not compile--does not like ()'s.

	// printf("x = %d\n", x); Note, x not available at this scope--outside if.

	// if (int c = 1 && int d == 2); Does not compile--only single variable can be declared.

    return 0;
}

In C#, we can use patterns to declare local variables within an expression, extending the declaration expression existing in C++:

namespace ConsoleApplication3
{
    class Program
    {
        static int foo() { return 1; }
        static void Main(string[] args)
        {
            if (foo() is var a) System.Console.WriteLine("a = {0}", a);

            // if ((int b = foo())) printf("\n"); does not compile.

            if (((1 == 2) is bool x) && ! x)
                System.Console.WriteLine("x = {0}", x);
            // else System.Console.WriteLine("x = {0}", x); // Does not compile "else" block--x is out of scope!

            if ((1 is int x) && x == 2)
                System.Console.WriteLine("x = {0}", x); // OK.

            // System.Console.WriteLine("x = {0}", x); // Note, x not available at this scope--outside if.

            if (1 is int c && 2 is int d)
                System.Console.WriteLine("c = {0}, d = {0}", c, d);
        }
    }
}

If the switch is rewritten as a nested if-then-else, the code works fine. Note, the scope of pattern matching variables is within the then clause, not the else clause of the if statement. I’m not sure why this would be the design, as in C/C++, the scope of declaration expression variables extend into the else clause. But perhaps it is to allow reuse of the same variable in a nested if-then-else.

namespace ConsoleApplication5
{
    class Program
    {
        static void Main(string[] args)
        {
            for (int j = 0; j < 20; ++j)
            {
                System.Console.WriteLine("j = " + j);
                if ((j is var i) && i % 2 == 0)
                {
                    System.Console.WriteLine("Even");
                } else if ((j is var i) && i > 10)
                {
                    System.Console.WriteLine("OK, > 10");
                } else
                {
                    System.Console.WriteLine("Anything else");
                }
            }
        }
    }
}

Further Information

https://www.infoq.com/news/2016/04/CSharp-7

Essential .NET – Designing C# 7, Mark Michaelis, December 2015 https://msdn.microsoft.com/en-us/magazine/mt595758.aspx

Pattern Matching for C# https://github.com/dotnet/roslyn/blob/future/docs/features/patterns.md

https://github.com/dotnet/roslyn/issues/2136

https://github.com/dotnet/roslyn/issues/206

https://github.com/dotnet/roslyn/blob/future/docs/features/patterns.md

Advanced Pattern Matching Features Removed From C# 7 https://www.infoq.com/news/2016/05/csharp7-pattern-matching-removed

C# 7 Features Previewed https://www.infoq.com/news/2016/04/CSharp-7

Unification: pattern matching, but twice as nice! By: on May 31, 2011  http://www.lshift.net/blog/2011/05/31/unification-pattern-matching-but-twice-as-nice/

https://github.com/dotnet/roslyn/blob/master/docs/Language%20Feature%20Status.md