Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> C#

Pattern Matching trong C# 7.0 là gì?

C# 7.0 giới thiệu pattern matching trong hai cases: is expression và switch statement.

Patterns test value có certain shape, và có thể extract information từ value khi nó có matching shape.

Pattern matching cung cấp concise syntax cho algorithms.

Có thể perform pattern matching trên bất kỳ data type nào, thậm chí custom types, trong khi if/else luôn cần primitives để match.

Pattern matching có thể extract values từ expression.

Trước Pattern Matching

public class PI { 
    public const float Pi = 3.142f; 
} 

public class Rectangle : PI { 
    public double Width { get; set; } 
    public double Height { get; set; } 
} 

public class Circle : PI { 
    public double Radius { get; set; } 
} 

class Program { 
    public static void PrintArea(PI pi) { 
        if (pi is Rectangle) { 
            Rectangle rectangle = pi as Rectangle; 
            System.Console.WriteLine("Area of Rect {0}", rectangle.Width * rectangle.Height); 
        } 
        else if (pi is Circle) { 
            Circle c = pi as Circle; 
            System.Console.WriteLine("Area of Circle {0}", Circle.Pi * c.Radius * c.Radius); 
        } 
    } 
    
    public static void Main() { 
        Rectangle r1 = new Rectangle { Width = 12.2, Height = 33 }; 
        Rectangle r2 = new Rectangle { Width = 12.2, Height = 44 }; 
        Circle c1 = new Circle { Radius = 12 }; 
        PrintArea(r1); 
        PrintArea(r2); 
        PrintArea(c1); 
        Console.ReadLine(); 
    } 
}

Output

Area of Rect 402.59999999999997
Area of Rect 536.8
Area of Circle 452.44799423217773

Sau Pattern Matching (C# 7.0)

public class PI { 
    public const float Pi = 3.142f; 
} 

public class Rectangle : PI { 
    public double Width { get; set; } 
    public double Height { get; set; } 
} 

public class Circle : PI { 
    public double Radius { get; set; } 
} 

class Program { 
    public static void PrintArea(PI pi) { 
        // Type pattern: is Type variable
        if (pi is Rectangle rectangle) { 
            System.Console.WriteLine("Area of Rect {0}", rectangle.Width * rectangle.Height); 
        } 
        else if (pi is Circle c) { 
            System.Console.WriteLine("Area of Circle {0}", Circle.Pi * c.Radius * c.Radius); 
        } 
    } 
    
    public static void Main() { 
        Rectangle r1 = new Rectangle { Width = 12.2, Height = 33 }; 
        Rectangle r2 = new Rectangle { Width = 12.2, Height = 44 }; 
        Circle c1 = new Circle { Radius = 12 }; 
        PrintArea(r1); 
        PrintArea(r2); 
        PrintArea(c1); 
        Console.ReadLine(); 
    } 
}

Output

Area of Rect 402.59999999999997
Area of Rect 536.8
Area of Circle 452.44799423217773

Pattern Matching Types (C# 7.0 - 9.0)

Pattern Type Version Syntax Ví dụ
Constant Pattern 7.0 constant case 0: case "hello": case null:
Type Pattern 7.0 Type varName case int i: case string s:
Var Pattern 7.0 var varName case var x: (always matches)
Declaration Pattern 7.0 Type varName if (x is int i)
Switch Expression 8.0 expr switch { ... } x switch { 1 => "one", _ => "other" }
Property Pattern 8.0 { Prop: pattern } { Age: > 18, Name: "John" }
Tuple Pattern 8.0 (p1, p2) (x, y) switch { (0,0) => ... }
Positional Pattern 8.0 Type(prop1, prop2) Point(0, 0) => "Origin"
Relational Patterns 9.0 <, >, <=, >= case >= 18:
Logical Patterns 9.0 and, or, not case > 0 and < 100:
List/Slice Patterns 11.0 [p1, p2, ..] [1, 2, ..] => "Starts with 1,2"

Advanced Examples

Switch Expression với Property Patterns (C# 8.0)

public string GetDiscount(Customer customer) => customer switch {
    { IsVip: true, YearsRegistered: > 5 } => "25% VIP Loyalty",
    { IsVip: true } => "15% VIP",
    { YearsRegistered: > 10 } => "10% Loyalty",
    { Age: < 18 } => "5% Student",
    _ => "0%"
};

Positional Patterns với Deconstruct (C# 8.0)

public record Point(int X, int Y);

public string Describe(Point p) => p switch {
    (0, 0) => "Origin",
    (0, _) => "On Y-axis",
    (_, 0) => "On X-axis",
    (var x, var y) when x == y => "On diagonal",
    _ => $"Point({p.X}, {p.Y})"
};

Nested Patterns

public record Order(int Id, Customer Customer, List Items);
public record Customer(string Name, Address Address);
public record Address(string City, string Country);

public string GetShippingInfo(Order order) => order switch {
    { Customer: { Address: { Country: "Vietnam" } } } => "Domestic shipping",
    { Customer: { Address: { Country: var country } } } => $"International: {country}",
    _ => "Unknown"
};

Pattern Matching với Null

public string GetName(Person person) => person switch {
    { Name: { Length: > 0 } name } => name,  // Non-null, non-empty
    { Name: null } => "Anonymous",
    null => "No person",
    _ => "Invalid"
};

Performance & Best Practices

  • Pattern matching được compile thành efficient IL (switch table, type checks)
  • Property patterns compile thành property access + pattern match
  • Tránh lạm dụng pattern matching phức tạp trong hot paths
  • Sử dụng _ (discard) cho default case thay vì var _
  • Exhaustiveness checking: Compiler cảnh báo nếu missing cases cho sealed types/enums