Chuyển tới nội dung chính

Control Flow

Định nghĩa (Definition)

Luồng điều khiển (Control Flow) đề cập đến thứ tự thực thi các câu lệnh, hướng dẫn hoặc lời gọi hàm riêng lẻ trong một chương trình. C# cung cấp câu lệnh điều kiện (Conditional Statements) (if, switch) để rẽ nhánh, vòng lặp (Loops) (for, foreach, while, do-while) để lặp lại, và câu lệnh nhảy (Jump Statements) (break, continue, return) để chuyển điều khiển.

Các khái niệm cốt lõi (Core Concepts)

if / else if / else

Câu lệnh if đánh giá một biểu thức Boolean (Boolean Expression) và thực thi một khối lệnh có điều kiện.

int score = 85;

if (score >= 90)
{
Console.WriteLine("Grade: A");
}
else if (score >= 80)
{
Console.WriteLine("Grade: B");
}
else if (score >= 70)
{
Console.WriteLine("Grade: C");
}
else
{
Console.WriteLine("Grade: F");
}
// Output: Grade: B
Thân câu lệnh đơn (Single-statement Bodies)

Khi thân chỉ có một câu lệnh, dấu ngoặc nhọn về mặt kỹ thuật là tùy chọn nhưng luôn sử dụng dấu ngoặc nhọn để dễ đọc và ngăn ngừa lỗi khi thêm dòng sau này.

Câu lệnh switch (Switch Statement)

Câu lệnh switch chọn một đoạn để thực thi từ danh sách các trường hợp dựa trên khớp mẫu (Pattern Match).

Switch truyền thống:

string dayOfWeek = DateTime.Now.DayOfWeek.ToString();

switch (dayOfWeek)
{
case "Monday":
case "Tuesday":
case "Wednesday":
case "Thursday":
case "Friday":
Console.WriteLine("Weekday");
break; // C# requires explicit break
case "Saturday":
case "Sunday":
Console.WriteLine("Weekend");
break;
default:
Console.WriteLine("Unknown");
break;
}

Switch với khớp mẫu (Pattern Matching) (C# 7+):

object value = 42;

switch (value)
{
case int i when i > 0:
Console.WriteLine($"Positive integer: {i}");
break;
case string s:
Console.WriteLine($"String of length {s.Length}");
break;
case null:
Console.WriteLine("Null value");
break;
default:
Console.WriteLine("Something else");
break;
}

Biểu thức switch (Switch Expressions) (C# 8+)

Biểu thức switch (Switch Expressions) ngắn gọn hơn và trả về một giá trị. Chúng sử dụng khớp mẫu (Pattern Matching) với cú pháp mũi tên =>.

string grade = score switch
{
>= 90 => "A",
>= 80 => "B",
>= 70 => "C",
>= 60 => "D",
_ => "F" // _ is the discard pattern (default)
};

Với mẫu tuple (Tuple Patterns):

string Classify(int x, int y) => (x, y) switch
{
(0, 0) => "Origin",
(> 0, > 0) => "Quadrant I",
(< 0, > 0) => "Quadrant II",
(< 0, < 0) => "Quadrant III",
(> 0, < 0) => "Quadrant IV",
(0, _) or (_, 0) => "On an axis"
};
Ưu tiên biểu thức switch (Switch Expressions)

Biểu thức switch (Switch Expressions) ngắn gọn hơn, hướng biểu thức (Expression-Oriented) và ít lỗi hơn câu lệnh switch (Switch Statements). Sử dụng chúng khi cần tạo giá trị từ rẽ nhánh nhiều hướng.

Toán tử ba ngôi (Ternary Operator) (? :)

Một điều kiện inline gọn nhẹ trả về một trong hai giá trị.

int age = 20;
string category = age >= 18 ? "Adult" : "Minor";

// Equivalent to:
string category;
if (age >= 18)
category = "Adult";
else
category = "Minor";

Vòng lặp (Loops)

Vòng lặp for

Sử dụng khi bạn biết số lần lặp trước.

for (int i = 0; i < 5; i++)
{
Console.WriteLine($"Iteration {i}");
}

Vòng lặp foreach

Sử dụng để duyệt qua các tập hợp (Collections) triển khai IEnumerable.

var fruits = new List<string> { "Apple", "Banana", "Cherry" };

foreach (var fruit in fruits)
{
Console.WriteLine(fruit);
}

Vòng lặp while

Sử dụng khi số lần lặp không xác định — kiểm tra điều kiện trước mỗi lần lặp.

int attempts = 0;
while (attempts < 3)
{
if (TryConnect())
break;
attempts++;
}

Vòng lặp do-while

Sử dụng khi thân vòng lặp phải thực thi ít nhất một lần — điều kiện được kiểm tra sau.

string input;
do
{
Console.Write("Enter 'yes' to continue: ");
input = Console.ReadLine();
} while (input != "yes");

break và continue

Câu lệnhTác dụng
breakThoát khỏi vòng lặp hoặc switch bao quanh gần nhất ngay lập tức
continueBỏ qua phần còn lại của lần lặp hiện tại và chuyển đến lần tiếp theo
for (int i = 0; i < 10; i++)
{
if (i == 3) continue; // Skip 3
if (i == 7) break; // Stop at 7
Console.Write($"{i} "); // Output: 0 1 2 4 5 6
}

goto

goto Label;
// ...
Label:
Console.WriteLine("Jumped here");
Tránh sử dụng goto

goto tồn tại trong C# nhưng hiếm khi cần thiết. Trường hợp sử dụng hợp lệ phổ biến duy nhất là chuyển điều khiển trong câu lệnh switch. Ưu tiên luồng điều khiển có cấu trúc (vòng lặp, break, phương thức) để rõ ràng và dễ bảo trì.

Ví dụ mã (Code Examples)

Biểu thức switch toàn diện (Comprehensive Switch Expression)

public enum HttpStatus { Ok, NotFound, ServerError, Unauthorized }

public static string GetMessageType(HttpStatus status) => status switch
{
HttpStatus.Ok => "Success",
HttpStatus.NotFound => "Resource not found",
HttpStatus.ServerError => "Internal error",
HttpStatus.Unauthorized => "Access denied",
_ => "Unknown status"
};

Vòng lặp lồng nhau với break và continue (Nested Loop with break and continue)

var matrix = new int[,] { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };

for (int row = 0; row < matrix.GetLength(0); row++)
{
for (int col = 0; col < matrix.GetLength(1); col++)
{
if (matrix[row, col] == 5) continue; // Skip 5
Console.Write($"{matrix[row, col]} ");
}
Console.WriteLine();
}

Lặp với liệt kê (Looping with Enumeration)

foreach (var (item, index) in fruits.Select((f, i) => (f, i)))
{
Console.WriteLine($"[{index}] {item}");
}

Khi nào sử dụng (When to Use)

Bảng quyết định rẽ nhánh (Branching Decision Table)

Tình huốngSử dụng
Rẽ nhánh hai hướng với điều kiện đơn giảnif / else
Nhiều giá trị rời rạc của một biến duy nhấtCâu lệnh switch
Rẽ nhánh nhiều hướng tạo ra một giá trịBiểu thức switch (Switch Expression)
Lựa chọn inline đơn giản hai giá trịBa ngôi (Ternary) ? :
Điều kiện Boolean phức tạpChuỗi if / else if

Bảng quyết định vòng lặp (Loop Decision Table)

Tình huốngSử dụng
Số lần lặp đã biết (cần chỉ mục)for
Duyệt qua tập hợp (Collection)foreach
Số lần lặp không xác định; có thể là 0while
Số lần lặp không xác định; phải chạy ít nhất một lầndo-while

Lỗi thường gặp (Common Pitfalls)

  • C# KHÔNG cho phép switch fall-through — Khác với C/C++, mọi case trong câu lệnh switch C# phải kết thúc bằng break, return, goto, hoặc throw. Các case trống chia sẻ cùng thân (ví dụ: case "Monday": case "Tuesday": ...) được phép vì chúng không phải fall-through thực sự.

  • Sửa đổi tập hợp (Collection) trong foreach — Bạn không thể thêm hoặc xóa phần tử khỏi tập hợp khi đang lặp bằng foreach. Nó sẽ ném InvalidOperationException. Thu thập các thay đổi và áp dụng sau vòng lặp, hoặc lặp qua một bản sao.

  • Lỗi sai một đơn vị (Off-by-one Errors) trong vòng lặp for — Sử dụng <= vs < trong điều kiện. Nhớ rằng mảng (Arrays) và danh sách (Lists) được đánh chỉ mục từ 0 (Zero-indexed): chỉ số hợp lệ cuối cùng là collection.Count - 1.

  • Vòng lặp while vô hạn — Luôn đảm bảo điều kiện vòng lặp sẽ trở thành false, hoặc bao gồm câu lệnh break với điều kiện bảo vệ (Guard).

  • Ba ngôi lồng nhau (Ternary Nesting) — Tránh nối chuỗi toán tử ba ngôi (a ? b ? c : d : e). Nó làm giảm khả năng đọc — sử dụng biểu thức switch (Switch Expressions) hoặc if/else thay thế.

Tóm tắt要点 (Key Takeaways)

  1. C# cung cấp if/else, câu lệnh switch (Switch Statements), biểu thức switch (Switch Expressions) và toán tử ba ngôi (Ternary Operator) để rẽ nhánh.
  2. Biểu thức switch (Switch Expressions) (C# 8+) ngắn gọn, trả về giá trị và hỗ trợ khớp mẫu (Pattern Matching) — ưu tiên chúng thay vì câu lệnh switch dài dòng.
  3. Sử dụng for cho lặp đếm, foreach cho tập hợp (Collections), while cho lặp không xác định số lần và do-while khi cần thực thi ít nhất một lần.
  4. C# cấm switch fall-through — mọi case phải thoát rõ ràng.
  5. Không bao giờ sửa đổi tập hợp (Collection) khi đang lặp bằng foreach.

Câu hỏi phỏng vấn (Interview Questions)

Q: C# có hỗ trợ switch fall-through không?

Không. Khác với C và C++, C# yêu cầu mọi khối case kết thúc bằng câu lệnh nhảy rõ ràng (break, return, goto, hoặc throw). Các case trống chia sẻ cùng thân được phép (ví dụ: case 1: case 2: ... break;), nhưng đây không phải fall-through — các case được nhóm lại.

Q: Sự khác biệt giữa breakcontinue là gì?

break ngay lập tức thoát khỏi vòng lặp hoặc câu lệnh switch bao quanh gần nhất. continue bỏ qua phần mã còn lại trong lần lặp hiện tại và chuyển đến lần lặp tiếp theo của vòng lặp. Cả hai đều không ảnh hưởng đến vòng lặp bên ngoài.

Q: Biểu thức switch (Switch Expressions) khác câu lệnh switch (Switch Statements) như thế nào?

Biểu thức switch (C# 8+) là biểu thức — chúng trả về giá trị và sử dụng cú pháp mũi tên =>. Chúng ngắn gọn hơn, hỗ trợ khớp mẫu (Pattern Matching) tự nhiên và sử dụng _ làm mẫu loại bỏ/mặc định (Discard/Default Pattern). Câu lệnh switch dựa trên câu lệnh, yêu cầu break trong mỗi case và không trả về giá trị trực tiếp. Biểu thức switch không thể chứa câu lệnh trong các nhánh của chúng.

Q: Điều gì xảy ra khi bạn sửa đổi tập hợp (Collection) trong vòng lặp foreach?

InvalidOperationException sẽ bị ném vì foreach sử dụng enumerator, và enumerator trở nên không hợp lệ khi tập hợp (Collection) cơ bản bị sửa đổi. Để giải quyết, lặp qua bản sao của tập hợp (ví dụ: foreach (var item in list.ToList())), hoặc thu thập các thay đổi và áp dụng sau vòng lặp.

Tài liệu tham khảo (References)