Mastering C# Pattern Combinators: Logical AND, OR, NOT, and Beyond with Examples

Dec 14, 2024·

3 min read

C# Pattern Combinator lets you make complex patterns from simple ones. Introduced in C# 8.0 and improved later, it helps you write clear and readable conditional logic. Let’s see some key pattern combinators and how to use them with examples.

What are Pattern Combinators?

Pattern combinators let you mix patterns using logical operators like AND, OR, and NOT. This enables you to check multiple conditions or rules quickly and clearly.

Key Pattern Combinators in C#:

1. Logical AND Pattern (and) (Introduced in C# 8.0)

The logical AND pattern matches when both patterns are satisfied. This is useful for validating multiple constraints on a single value.

Syntax:

if (input is > 0 and < 100)
{
    Console.WriteLine("Input is between 1 and 99.");
}

Example Use Case:

To validate a number is within a specific range.

2. Logical OR Pattern (or) (Introduced in C# 9.0)

The logical OR pattern matches when at least one pattern is satisfied. This is ideal for handling multiple acceptable cases.

Syntax:

if (input is 0 or 100)
{
    Console.WriteLine("Input is a boundary value (0 or 100).");
}

Example Use Case:

To check if a number equals one of two specific values.

3. Logical NOT Pattern (not) (Introduced in C# 9.0)

The logical NOT pattern matches when the input does not match the specified pattern. This is especially handy for exclusion scenarios.

Syntax:

if (input is not null)
{
    Console.WriteLine("Input is not null.");
}

Example Use Case:

To ensure an input is not null.

4. Parenthesized Pattern (Introduced in C# 9.0)

Parenthesized patterns group patterns together to control precedence or improve clarity in complex expressions.

Syntax:

if (input is not (null or ""))
{
    Console.WriteLine("Input is not null or empty.");
}

Example Use Case:

To combine multiple conditions in a readable way.

5. Relational Patterns with Combinators (Introduced in C# 8.0)

Relational patterns (e.g., >, <, >=, <=) can be combined with logical patterns to create even more specific conditions.

Syntax:

if (input is >= 0 and <= 10 or > 20)
{
    Console.WriteLine("Input is in range 0-10 or greater than 20.");
}

Example Use Case:

To handle overlapping or complex numeric ranges.

Pattern Matching in Switch Expressions

Switch expressions allow you to leverage pattern combinators in a concise and declarative manner.

Example:

string GetStatus(int score) => score switch
{
    >= 90 and <= 100 => "Excellent",
    >= 75 and < 90 => "Good",
    >= 50 and < 75 => "Average",
    < 50 => "Fail",
    _ => "Invalid"
};

Advanced Use Cases

Matching Tuples

Combine tuple patterns with pattern combinators to handle structured data.

if ((x, y) is (0, 0) or (_, 1))
{
    Console.WriteLine("Either at origin or on the y-axis.");
}

Using Type Patterns

Match specific types while applying additional conditions.

if (obj is string s and not "")
{
    Console.WriteLine($"Non-empty string: {s}");
}

Recursive Patterns

Use recursive patterns to traverse and match data structures.

if (tree is Node { Left: null, Right: not null })
{
    Console.WriteLine("Tree has a single child on the right.");
}

Why Use Pattern Combinators?

  • Readability: Simplify complex conditions and remove nested if-else blocks.

  • Maintainability: Easier to update and extend logic.

  • Expressiveness: Handle sophisticated scenarios with minimal code.

Conclusion

C# Pattern Combinators enables you to write cleaner and more expressive code. By combining simple patterns with AND, OR, and NOT, you can solve complex problems ease. Start using pattern combinators to improve your code quality.

For further reading, check out the official Microsoft documentation on C# Pattern Matching.