Pattern Matching
Định nghĩa (Definition)
Khớp mẫu (Pattern Matching) là cơ chế kiểm tra giá trị theo các hình dạng (mẫu) và trích xuất thông tin từ chúng. Được giới thiệu trong C# 7 và mở rộng đáng kể trong các phiên bản sau, khớp mẫu thay thế các chuỗi if-else dài dòng và câu lệnh switch bằng cú pháp ngắn gọn, biểu cảm, kết hợp kiểm tra kiểu (type checking), so sánh giá trị (value comparison), và trích xuất dữ liệu (data extraction) vào một cấu trúc duy nhất.
Khái niệm cốt lõi (Core Concepts)
Biểu thức is (The is Expression)
Toán tử is kiểm tra xem một biểu thức có khớp với mẫu hay không. Nó có thể kiểm tra kiểu, khai báo biến, và kết hợp với toán tử quan hệ/logic.
// Type pattern — test and cast
if (obj is string s)
Console.WriteLine(s.Length);
// Declaration pattern — extract value
if (value is int number && number > 0)
Console.WriteLine($"Positive: {number}");
// Negation pattern
if (value is not null)
Console.WriteLine(value);
Biểu thức Switch (Switch Expressions - C# 8+)
Biểu thức switch (switch expression) là dạng ngắn gọn, dựa trên biểu thức của switch trả về một giá trị. Mỗi nhánh (arm) là một mẫu theo sau bởi => và kết quả.
string Classify(int value) => value switch
{
< 0 => "Negative",
0 => "Zero",
> 0 and <= 100 => "Small positive",
> 100 => "Large positive",
};
Kiểu Mẫu (Type Patterns)
Khớp dựa trên kiểu thời gian chạy (runtime type) của đối tượng và tùy chọn liên kết biến:
string Describe(object shape) => shape switch
{
Circle c => $"Circle with radius {c.Radius}",
Rectangle r => $"Rectangle {r.Width}x{r.Height}",
Triangle => "A triangle",
null => "No shape",
_ => "Unknown shape"
};
Mẫu Thuộc tính (Property Patterns)
Khớp với các thuộc tính của đối tượng sử dụng cú pháp { Property: pattern }:
decimal CalculateDiscount(Order order) => order switch
{
{ Customer.IsVip: true, Total: > 1000m } => 0.20m,
{ Customer.IsVip: true } => 0.10m,
{ Total: > 500m } => 0.05m,
_ => 0m
};
C# 10 mở rộng mẫu thuộc tính cho phép truy cập lồng nhau mà không cần lặp dấu ngoặc nhọn:
// C# 10 extended property pattern
if (order is { Customer.IsVip: true, Items.Count: > 5 })
ApplyBulkDiscount(order);
Mẫu Vị trí (Positional Patterns)
Hoạt động với các kiểu hỗ trợ phân cấu (deconstruction) (thông qua phương thức Deconstruct hoặc record vị trí):
record Point(double X, double Y);
string Classify(Point p) => p switch
{
(0, 0) => "Origin",
(0, _) => "On Y-axis",
(_, 0) => "On X-axis",
( > 0, > 0) => "First quadrant",
_ => "Elsewhere"
};
Mẫu Quan hệ (Relational Patterns)
So sánh với giá trị hằng sử dụng <, >, <=, >=:
string GetGrade(int score) => score switch
{
>= 90 => "A",
>= 80 => "B",
>= 70 => "C",
>= 60 => "D",
_ => "F"
};
Mẫu Logic (Logical Patterns)
Kết hợp mẫu với and, or, not:
bool IsLetter(char c) => c is (>= 'a' and <= 'z') or (>= 'A' and <= 'Z');
bool IsNotValid(int? age) => age is null or < 0 or > 150;
Mẫu Danh sách (List Patterns - C# 11)
Khớp các phần tử trong mảng và danh sách. Sử dụng [..] cho mẫu cắt (slice pattern):
int SumFirstTwo(int[] numbers) => numbers switch
{
[] => 0,
[var a] => a,
[var a, var b] => a + b,
[var a, var b, ..] => a + b,
};
string ClassifyResponse(int[] codes) => codes switch
{
[200] => "OK",
[200, 200] => "All OK",
[400, ..] => "Client error prefix",
[>= 500] => "Server error",
_ => "Unknown"
};
Ví dụ Mã (Code Examples)
Máy trạng thái với Khớp mẫu (State Machine with Pattern Matching)
enum State { Open, Closed, Locked }
enum Action { Open, Close, Lock, Unlock }
State Transition(State current, Action action) => (current, action) switch
{
(State.Open, Action.Close) => State.Closed,
(State.Closed, Action.Open) => State.Open,
(State.Closed, Action.Lock) => State.Locked,
(State.Locked, Action.Unlock) => State.Closed,
_ => current // No transition
};
Xác thực Dữ liệu (Data Validation)
static string ValidateUser(User user) => user switch
{
null => "User is required",
{ Name: null or "" } => "Name is required",
{ Age: < 0 or > 150 } => "Invalid age",
{ Email: not string { Length: > 0 } } => "Email is required",
_ => "Valid"
};
Khi nào Sử dụng (When to Use)
- Thay thế chuỗi if-else — biểu thức switch dễ đọc hơn khi phân nhánh trên cùng một giá trị
- Kiểm tra kiểu và ép kiểu —
is Type varan toàn và gọn hơntypeof+ ép kiểu (cast) - Xác thực dữ liệu — mẫu thuộc tính thể hiện rõ ràng các ràng buộc cấu trúc
- Máy trạng thái (State machine) — mẫu tuple trên cặp
(trạng thái, sự kiện)làm cho chuyển đổi mang tính khai báo - Phân cấu record và tuple — mẫu vị trí phù hợp tự nhiên với kiểu record
- Xử lý trường hợp null —
is null,is not nulltích hợp với mọi mẫu khác