Search code examples
c#pattern-matchinglinq-expressions

Pattern matching with Expression<Func<T, bool>>


Consider the following code:

internal static class Program
{
    private static void Main()
    {
        object value = "Hello";
        Console.WriteLine(value.Test(o => o is string));
    }

    private static bool Test<T>(this T value, Expression<Func<T, bool>> test) => 
        test.Compile().Invoke(value);
}

Result:

True

Now, compare to this code:

internal static class Program
{
    private static void Main()
    {
        string? value = "Hello";
        Console.WriteLine(value.Test(o => o is null)); // Compiler error
    }

    private static bool Test<T>(this T value, Expression<Func<T, bool>> test) =>
        test.Compile().Invoke(value);
}

Result:

An expression tree cannot contain pattern-matching 'is' expression

I'm curious as to why the latter code produces the above compiler error, but the former code does not, even though it too is also a pattern-matching 'is' expression; i.e. why is type matching allowed, but null matching isn't?

Note: I know that I could use Func<T, bool> instead of Expression<Func<T, bool>> but that is beyond the scope of this question.


Solution

  • The usage of is as a type test (obj is Foo) is very old, and comes from the earliest days of C#.

    The is keyword was later expanded to include patterns (obj is null, obj is string s, etc), first in C# 7, and then further in subsequent versions.

    Expressions are stuck at C# 3 for backwards compatibility reasons, so they support type tests but not patterns.