C# > Functional Programming > Pattern Matching > Relational and Logical Patterns

Pattern Matching with Relational and Logical Patterns in C#

This snippet demonstrates how to use relational and logical patterns in C# for more expressive and concise conditional logic. These patterns allow you to match values against ranges and combine multiple conditions for more complex matching scenarios.

Code Snippet: Relational and Logical Patterns

This code defines two methods, `GetGrade` and `ClassifyTemperature`, demonstrating relational and logical patterns. `GetGrade` evaluates a numeric score and returns a letter grade using relational patterns (`>=`, `<`) and logical patterns (`and`). `ClassifyTemperature` takes a temperature and a boolean indicating whether it's Celsius. It then uses tuple patterns combined with relational and logical patterns to classify the temperature as Hot, Moderate, or Cold. The `Main` method showcases the usage with several example values.

using System;

public class PatternMatchingExample
{
    public static string GetGrade(int score)
    {
        return score switch
        {
            >= 90 => "A",
            >= 80 and < 90 => "B",
            >= 70 and < 80 => "C",
            >= 60 and < 70 => "D",
            < 60 => "F",
            _ => "Invalid Score"
        };
    }

    public static string ClassifyTemperature(double temperature, bool isCelsius)
    {
        return (temperature, isCelsius) switch
        {
            (> 30, true) => "Hot (Celsius)",
            (> 86, false) => "Hot (Fahrenheit)",
            (>= 20 and <= 30, true) => "Moderate (Celsius)",
            (>= 68 and <= 86, false) => "Moderate (Fahrenheit)",
            (< 20, true) => "Cold (Celsius)",
            (< 68, false) => "Cold (Fahrenheit)",
            _ => "Unknown Temperature"
        };
    }

    public static void Main(string[] args)
    {
        Console.WriteLine($"Grade for 95: {GetGrade(95)}"); // Output: A
        Console.WriteLine($"Grade for 75: {GetGrade(75)}"); // Output: C
        Console.WriteLine($"Temperature Classification: {ClassifyTemperature(32, true)}"); // Output: Hot (Celsius)
        Console.WriteLine($"Temperature Classification: {ClassifyTemperature(50, false)}"); // Output: Cold (Fahrenheit)
    }
}

Concepts Behind the Snippet

Pattern matching is a powerful feature introduced in C# 7.0 and enhanced in subsequent versions. It allows you to test an expression against a pattern and, if the expression matches the pattern, extract values from the expression. Relational patterns enable matching based on comparison operators (e.g., >, <, >=, <=). Logical patterns combine multiple patterns using logical operators like `and`, `or`, and `not` to create more complex matching conditions. Using `switch` expressions makes the code more readable and maintainable compared to nested `if-else` statements.

Real-Life Use Case

Relational and logical patterns are useful in scenarios where you need to categorize data based on ranges and multiple conditions. Consider a financial application that calculates tax brackets based on income. You could use relational and logical patterns to define different tax brackets based on income ranges and filing status. Another example is in game development to categorize the type of damage based on the magnitude of the impact and the materials involved.

Best Practices

  • Keep patterns readable: Avoid overly complex patterns that are difficult to understand.
  • Handle all possible cases: Ensure that your pattern matching covers all possible input values, either explicitly or with a discard pattern (`_`).
  • Use descriptive variable names: When extracting values from patterns, use meaningful variable names to improve code clarity.
  • Avoid redundant checks: Use logical patterns to combine conditions and avoid redundant checks.

Interview Tip

Be prepared to explain how pattern matching can improve code readability and maintainability. Highlight the benefits of using relational and logical patterns for complex conditional logic. Mention how these patterns can simplify code that would otherwise require nested `if-else` statements. Also, discuss the performance implications: while pattern matching is generally efficient, excessive complexity can impact performance, so strive for balance.

When to Use Them

Use relational and logical patterns when you need to:

  • Match values against ranges.
  • Combine multiple conditions for pattern matching.
  • Simplify complex conditional logic.
  • Improve code readability and maintainability.

Memory Footprint

Pattern matching itself doesn't introduce significant memory overhead. The memory footprint is primarily determined by the data types involved in the matching process. However, using complex patterns or extracting large amounts of data can indirectly increase memory usage. Optimize the complexity and data extraction to mitigate potential memory concerns.

Alternatives

Alternatives to relational and logical patterns include:

  • Nested `if-else` statements: Traditional conditional logic can be used to achieve similar results, but it can lead to less readable and maintainable code.
  • Lookup tables: For simple range-based categorization, lookup tables (e.g., dictionaries) can be an efficient alternative.

Pros

  • Improved readability: Pattern matching makes conditional logic more expressive and easier to understand.
  • Conciseness: Complex conditions can be expressed more concisely than with traditional `if-else` statements.
  • Maintainability: Pattern matching encourages a more structured and organized approach to conditional logic, making code easier to maintain.

Cons

  • Potential complexity: Overly complex patterns can be difficult to understand and debug.
  • Learning curve: Developers unfamiliar with pattern matching may require some time to learn the syntax and concepts.

FAQ

  • What happens if none of the patterns match?

    If none of the patterns match in a `switch` expression, the discard pattern (`_`) can be used as a default case. If no discard pattern is present and none of the patterns match, a `MatchError` exception is thrown. It's crucial to handle all possible cases to avoid unexpected errors.
  • Can I use relational and logical patterns with other types besides numbers?

    Yes, you can use relational and logical patterns with other types like strings and dates, provided that the types support the necessary comparison operators and logical operations.
  • Are there performance considerations when using pattern matching?

    While pattern matching is generally efficient, excessively complex patterns can impact performance. Simplify patterns where possible and avoid unnecessary computations within the matching logic.