1. Giới thiệu
Bạn đã biết viết code kiểu này rồi nè:
switch (color)
{
case "red":
Console.WriteLine("Dừng lại!");
break;
case "yellow":
Console.WriteLine("Chờ tí...");
break;
case "green":
Console.WriteLine("Đi được rồi!");
break;
default:
Console.WriteLine("Màu gì đây?");
break;
}
Nhưng nếu muốn gán giá trị cho biến tuỳ vào tham số truyền vào, ví dụ trả về chuỗi dựa trên số từ function thì sao? Trước khi có switch-biểu thức thì phải viết kiểu này nè:
string meaning;
switch (number)
{
case 42:
meaning = "Câu trả lời cho câu hỏi lớn nhất về cuộc sống, vũ trụ và mọi thứ";
break;
case 7:
meaning = "Số may mắn";
break;
default:
meaning = "Số bình thường";
break;
}
Kiểu này nhìn không được sang chảnh cho lắm nhỉ? Muốn trả về giá trị luôn cho gọn, giống mấy ngôn ngữ functional ấy.
Đó là lúc switch-biểu thức xuất hiện — một trong những tính năng xịn (và mới) của C# từ version 8.0 trở lên.
2. switch-biểu thức: cú pháp nhìn là muốn ôm
So sánh: operator và expression
switch cổ điển là statement ("operator"), còn switch-biểu thức là expression, tức là nó tính ra giá trị và có thể gán luôn cho biến hoặc trả về từ method.
Cấu trúc switch-biểu thức:
var result = value switch
{
pattern1 => expression1,
pattern2 => expression2,
_ => defaultExpression // "gạch dưới" là "còn lại"
};
Ví dụ nè:
int number = 42;
string meaning = number switch
{
42 => "Câu trả lời cho câu hỏi lớn nhất về cuộc sống, vũ trụ và mọi thứ",
7 => "Số may mắn",
_ => "Số bình thường"
};
Console.WriteLine(meaning); // => Câu trả lời cho câu hỏi lớn nhất về cuộc sống, vũ trụ và mọi thứ
Gọn, dễ đọc, không có code dài dòng, không cần break; nào luôn.
Lưu ý: gạch dưới _ là "bắt tất cả trường hợp còn lại", giống default.
3. switch-biểu thức và pattern: kết hợp cực đã
Đây mới là phần vui nè. Trong switch-biểu thức bạn không chỉ dùng giá trị thường, mà còn dùng pattern: kiểm tra theo khoảng, theo kiểu, theo thuộc tính object, deconstruct (tách object thành phần), thậm chí combinatorial patterns (and, or, not).
Ví dụ pattern trong switch-biểu thức
Pattern hằng số
Cái này ở trên làm rồi: so sánh với 42, 7 v.v.
Khoảng và so sánh
int age = 17;
string category = age switch
{
< 18 => "Chưa đủ tuổi",
>= 18 and <= 65 => "Người lớn",
> 65 => "Người cao tuổi",
_ => "Không rõ độ tuổi"
};
Console.WriteLine(category);
Nhìn nè — có thể dùng nhiều điều kiện cùng lúc! C# ngày càng biểu cảm hơn.
Pattern kiểu (Type Patterns)
Giả sử có object có thể là nhiều kiểu (ví dụ object value;). Xử lý từng kiểu như này:
object value = 3.14;
string description = value switch
{
int i => $"Số nguyên: {i}",
double d => $"Số thực: {d:F2}",
string s => $"Chuỗi: '{s}'",
null => "null!",
_ => "Cái gì đó khác"
};
Console.WriteLine(description); // => Số thực: 3.14
Pattern thuộc tính (Property Patterns và Positional Patterns)
Từ C# 8 trở đi, có thể kiểm tra giá trị thuộc tính ngay trong switch-biểu thức.
Ví dụ với object
public class User
{
public string Name { get; set; }
public int Age { get; set; }
}
User user = new User { Name = "Anna", Age = 17 };
string welcome = user switch
{
{ Age: < 18 } => $"Chào, {user.Name}! Bạn là teen.",
{ Age: >= 18 and <= 65 } => $"Hi, {user.Name}! Cuộc sống người lớn tuyệt vời lắm.",
{ Age: > 65 } => $"Chào mừng, {user.Name}! Lúc nào cũng hoan nghênh bạn.",
_ => "Không rõ tuổi"
};
Console.WriteLine(welcome);
Ở đây trong dấu ngoặc nhọn { Age: < 18 } là property pattern, tức là chỉ kiểm tra thuộc tính thôi, không phải cả object.
4. Pattern mới trong C# 11-14
C# ngày càng gọn và linh hoạt. Gần đây có thêm combinatorial patterns: and, or, not, và positional patterns cũng ngon hơn. Cùng xem thử nhé.
Pattern logic: and, or, not
Bạn có thể tạo điều kiện phức tạp như logic toán học luôn!
int temperature = 25;
string result = temperature switch
{
< 0 => "Lạnh cóng!",
>= 0 and < 20 => "Mát mẻ",
>= 20 and < 30 => "Dễ chịu",
>= 30 or <= -30 => "Cực đoan!",
_ => "Nhiệt độ lạ"
};
Muốn loại trừ trường hợp nào đó? Dễ thôi:
int score = 80;
string grade = score switch
{
>= 90 => "Xuất sắc",
>= 60 and not >= 90 => "Tốt",
_ => "Tệ"
};
Deconstruct (Positional Patterns)
Nếu bạn có struct hoặc record hỗ trợ deconstruct, C# cho phép tách object ngay trong switch-biểu thức:
public record Point(int X, int Y);
Point p = new Point(10, 20);
string desc = p switch
{
(0, 0) => "Gốc toạ độ",
(var x, 0) => $"Trên trục X, X={x}",
(0, var y) => $"Trên trục Y, Y={y}",
(var x, var y) when x == y => $"Trên đường chéo: {x}",
_ => $"Tại điểm ({p.X}, {p.Y})"
};
Console.WriteLine(desc); // => Tại điểm (10, 20)
5. Khi nào và tại sao nên dùng switch-biểu thức
Trong C# hiện đại, switch-biểu thức không chỉ là "tính năng hot" mà còn giúp code gọn, dễ test, dễ bảo trì. Đặc biệt hữu ích khi viết parser, xử lý command, business logic, cấu hình rule — nói chung là khi cần chia nhánh theo tiêu chí phức tạp, không chỉ theo giá trị mà còn kiểu, khoảng, thuộc tính, kết hợp các thứ.
Khi phỏng vấn
- Bạn sẽ xử lý các trường hợp trả lời khác nhau từ user thế nào?
- Làm sao trả về giá trị function tuỳ cả kiểu lẫn giá trị?
- Làm sao thêm rule mới mà không phải viết cả đống if-else?
Show switch-biểu thức với pattern đi! Thế là bạn chứng minh luôn biết tính năng mới và code style xịn.
Bảng: so sánh switch-operator và switch-biểu thức
| Đặc điểm | switch statement | switch expression |
|---|---|---|
| Kiểu | operator (statement) | biểu thức (expression) |
| Dùng break | bắt buộc | không cần |
| Có trả về giá trị không | chỉ qua biến | có, trả về luôn |
| Dùng pattern được không | một phần | đầy đủ (type, property, v.v.) |
| Bẫy | có thể quên break | mọi nhánh phải trả về giá trị |
6. Lỗi thường gặp và mấy trò nhỏ nhỏ
- Dev mới hay nhầm giữa switch-operator ("statement") và switch-biểu thức ("expression"). Ví dụ, quên là trong switch-biểu thức không cần break;, và mọi nhánh phải trả về giá trị.
- Nếu quên nhánh _, compiler sẽ nhắc: "nếu không khớp cái nào thì sao?".
- Trong switch-biểu thức không dùng được goto (may quá!).
- Nếu trùng điều kiện (ví dụ hai pattern giống nhau), compiler cũng không cho qua đâu.
- Chú ý kiểu giá trị trả về từ switch-biểu thức: phải hợp với kiểu biến bạn gán vào nhé.
GO TO FULL VERSION