C# -

Pattern Matching

Pattern matching in C# is a powerful feature that allows you to inspect and deconstruct values in a declarative way. This tutorial covers the basics, advanced concepts, and best practices for using pattern matching in C#.


1. Introduction to Pattern Matching

Pattern matching allows you to check a value against a pattern. It is commonly used with switch statements and expressions, but can also be used in other contexts.

        
            using System;

namespace PatternMatchingExamples;

public class Program
{
    public static void Main(string[] args)
    {
        object obj = 5;
        if (obj is int i)
        {
            Console.WriteLine($"Integer: {i}");
        }
    }
}
        
    

2. Type Patterns

Type patterns allow you to check the type of an object and cast it to that type if the pattern matches.

        
            using System;

namespace PatternMatchingExamples;

public class Program
{
    public static void Main(string[] args)
    {
        object obj = "Hello, World!";
        if (obj is string s)
        {
            Console.WriteLine($"String: {s}");
        }
    }
}
        
    

3. Constant Patterns

Constant patterns allow you to check if a value is equal to a constant.

        
            using System;

namespace PatternMatchingExamples;

public class Program
{
    public static void Main(string[] args)
    {
        int number = 5;
        if (number is 5)
        {
            Console.WriteLine("Number is 5");
        }
    }
}
        
    

4. Var Patterns

The var pattern matches any value and binds it to a new variable.

        
            using System;

namespace PatternMatchingExamples;

public class Program
{
    public static void Main(string[] args)
    {
        object obj = 42;
        if (obj is var x)
        {
            Console.WriteLine($"Value: {x}");
        }
    }
}
        
    

5. Discard Patterns

Discard patterns match any value but do not bind it to a variable. They are useful when you need to ignore certain values.

        
            using System;

namespace PatternMatchingExamples;

public class Program
{
    public static void Main(string[] args)
    {
        object obj = 100;
        if (obj is int _)
        {
            Console.WriteLine("Object is an integer");
        }
    }
}
        
    

6. Property Patterns

Property patterns allow you to match on the properties of an object. This is useful for deconstructing objects and checking their properties.

        
            using System;

namespace PatternMatchingExamples;

public class Program
{
    public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }

    public static void Main(string[] args)
    {
        var person = new Person { Name = "Alice", Age = 30 };
        if (person is { Name: "Alice", Age: 30 })
        {
            Console.WriteLine("Matched Alice, 30 years old");
        }
    }
}
        
    

7. Positional Patterns

Positional patterns allow you to deconstruct an object using its deconstruction method or positional properties.

        
            using System;

namespace PatternMatchingExamples;

public class Program
{
    public class Point
    {
        public int X { get; }
        public int Y { get; }

        public Point(int x, int y)
        {
            X = x;
            Y = y;
        }

        public void Deconstruct(out int x, out int y)
        {
            x = X;
            y = Y;
        }
    }

    public static void Main(string[] args)
    {
        var point = new Point(1, 2);
        if (point is (1, 2))
        {
            Console.WriteLine("Point is (1, 2)");
        }
    }
}
        
    

8. Tuple Patterns

Tuple patterns allow you to match tuples and deconstruct their values.

        
            using System;

namespace PatternMatchingExamples;

public class Program
{
    public static void Main(string[] args)
    {
        var tuple = (1, "Hello");
        if (tuple is (1, "Hello"))
        {
            Console.WriteLine("Tuple matched (1, \"Hello\")");
        }
    }
}
        
    

9. Relational Patterns

Relational patterns allow you to compare a value to another value using relational operators.

        
            using System;

namespace PatternMatchingExamples;

public class Program
{
    public static void Main(string[] args)
    {
        int number = 42;
        if (number is > 40)
        {
            Console.WriteLine("Number is greater than 40");
        }
    }
}
        
    

10. Logical Patterns

Logical patterns allow you to combine patterns using logical operators such as and, or, and not.

        
            using System;

namespace PatternMatchingExamples;

public class Program
{
    public static void Main(string[] args)
    {
        int number = 42;
        if (number is > 30 and < 50)
        {
            Console.WriteLine("Number is between 30 and 50");
        }
    }
}
        
    

11. Pattern Matching in Switch Statements

Pattern matching can be used in switch statements to create more readable and concise code.

        
            using System;

namespace PatternMatchingExamples;

public class Program
{
    public static void Main(string[] args)
    {
        object obj = 42;
        switch (obj)
        {
            case int i:
                Console.WriteLine($"Integer: {i}");
                break;
            case string s:
                Console.WriteLine($"String: {s}");
                break;
            default:
                Console.WriteLine("Unknown type");
                break;
        }
    }
}
        
    

12. Pattern Matching in Switch Expressions

Switch expressions provide a more concise syntax for pattern matching compared to switch statements.

        
            using System;

namespace PatternMatchingExamples;

public class Program
{
    public static void Main(string[] args)
    {
        object obj = 42;
        var result = obj switch
        {
            int i => $"Integer: {i}",
            string s => $"String: {s}",
            _ => "Unknown type"
        };

        Console.WriteLine(result);
    }
}
        
    

13. Pattern Matching with Lists

You can use pattern matching with lists to match on the structure and contents of lists.

        
            using System;
using System.Collections.Generic;

namespace PatternMatchingExamples;

public class Program
{
    public static void Main(string[] args)
    {
        var list = new List<int> { 1, 2, 3 };
        if (list is [1, 2, 3])
        {
            Console.WriteLine("List matched [1, 2, 3]");
        }
    }
}
        
    

14. Advanced Pattern Matching Techniques

Advanced pattern matching techniques include combining multiple patterns, using custom patterns, and optimizing pattern matching performance.

        
            using System;

namespace PatternMatchingExamples;

public class Program
{
    public static void Main(string[] args)
    {
        object obj = new Person { Name = "Alice", Age = 30 };
        switch (obj)
        {
            case Person { Name: "Alice", Age: > 20 }:
                Console.WriteLine("Matched Alice, older than 20");
                break;
            case Person { Name: "Bob", Age: < 20 }:
                Console.WriteLine("Matched Bob, younger than 20");
                break;
            default:
                Console.WriteLine("Unknown person");
                break;
        }
    }

    public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }
}
        
    


15. Conclusion

Pattern matching in C# is a versatile feature that allows you to write more expressive and concise code. By understanding and using pattern matching effectively, you can simplify your code and make it more readable.