Menu

Câu lệnh switch trong C#: case, break, default và nhiều case

Cách câu lệnh switch hoạt động trong C#: switch trên giá trị int, string, char và enum, vì sao mọi case cần break (lỗi CS0163), xếp chồng nhãn case cho nhiều giá trị, goto case, default, phân biệt hoa thường với chuỗi, và nhãn case dạng pattern với when.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

Câu lệnh switch so sánh một giá trị với danh sách các nhãn case hằng số và chạy phần khớp với giá trị đó. Trong C#, mọi phần đều phải kết thúc bằng break (hoặc một câu lệnh khác giúp thoát khỏi phần đó), nên một case không bao giờ có thể lặng lẽ chạy sang case kế tiếp.

Đầu ra:

200: OK
302: redirect
404: not found
500: unhandled status

Giá trị trong ngoặc được tính một lần rồi so sánh với từng nhãn. default chạy khi không có gì khớp; nó không bắt buộc, và có thể đặt ở bất kỳ đâu trong danh sách, dù thường được đặt cuối cùng. Nếu không có gì khớp và không có default, cả câu lệnh switch không làm gì.

Mỗi phần đều cần break

Trong C, C++, Java và JavaScript, quên break khiến chương trình trượt xuống case tiếp theo. C# biến lỗi đó thành lỗi biên dịch:

switch (level)
{
    case 1:
        Console.WriteLine("read");
        // error CS0163: Control cannot fall through from one case label ('case 1:') to another
    case 2:
        Console.WriteLine("write");
        break;
}

Quy tắc là điểm cuối danh sách câu lệnh của một phần không được phép chạm tới. break là cách thông dụng để thỏa mãn quy tắc này, nhưng bất cứ thứ gì thoát khỏi phần đó đều được:

  • return thoát khỏi toàn bộ phương thức, thường là cách gọn nhất trong một hàm hỗ trợ nhỏ.
  • throw ném ngoại lệ cho một giá trị lẽ ra không bao giờ xuất hiện.
  • continue nhảy sang lần lặp tiếp theo khi switch nằm trong một vòng lặp.
  • goto case và goto default cố ý nhảy sang một phần khác.

Phần cuối cùng cũng không được miễn. Một default: ở cuối mà không có break sẽ lỗi CS8070, Control cannot fall out of switch from final case label ('default:').

Nhiều giá trị trong một case

Các nhãn không có câu lệnh nào xen giữa sẽ dùng chung phần bên dưới. Đây là kiểu fall through duy nhất mà C# cho phép, vì một nhãn rỗng không thể giấu code nào:

Đầu ra:

'O' is a vowel
'k' is a consonant or symbol
' ' is a space
'7' is a digit

Mỗi phần ở đây kết thúc bằng return, nên không cần break (một break sau return sẽ là code không thể chạm tới). Hai nhãn cùng giá trị là lỗi, CS0152 The switch statement contains multiple cases with the label value '1', và mọi nhãn phải là hằng số lúc biên dịch: đặt một biến trong case sẽ lỗi "a constant value is expected".

Switch trên chuỗi

Sau số nguyên, chuỗi là giá trị được switch nhiều nhất. Phép so sánh là ordinal và phân biệt hoa thường, nên hãy chuẩn hóa đầu vào trước khi switch:

Đầu ra:

Starting the server
Stopping the server
Server is running
Unknown command: restart
Unknown command: (none)

Nếu không có ToLowerInvariant, "STOP" sẽ rơi vào default. Switch trên giá trị null là hợp lệ: nó không khớp hằng số chuỗi nào và rơi vào default, nên bản thân switch không bao giờ ném ngoại lệ.

Switch trên enum

Enum chính là thứ mà switch được tạo ra để xử lý: một tập đóng các giá trị có tên.

Đầu ra:

Pending: wait for payment
Paid: pack and ship
Shipped: track the parcel
Delivered: nothing, order is closed
Cancelled: nothing, order is closed
ArgumentOutOfRangeException for value 42

Trình biên dịch không kiểm tra xem câu lệnh switch đã bao phủ mọi thành viên enum hay chưa, và một biến enum có thể chứa bất kỳ số nguyên nào của kiểu nền ((OrderStatus)42 là hợp lệ). Nhánh default ném ngoại lệ biến một case bị bỏ quên thành lỗi xuất hiện ngay và dễ thấy, thay vì lặng lẽ không làm gì.

goto case và goto default

Khi một case thật sự nên chạy tiếp sang case khác, C# buộc bạn viết rõ lệnh nhảy:

Đầu ra:

admin:
  manage users
  edit posts
  read posts
editor:
  edit posts
  read posts
guest:
  read posts

goto case nhận một hằng số phải khớp với một trong các nhãn của cùng câu lệnh switch. Cách này hiếm gặp trong code thực tế: hành vi dùng chung thường rõ ràng hơn khi đặt trong một phương thức mà nhiều case cùng gọi. Dù vậy, đây vẫn là câu trả lời cho câu hỏi "làm sao fall through trong C#".

Biến bên trong case

Toàn bộ khối switch là một phạm vi, nên hai case không thể khai báo biến cùng tên:

switch (shape)
{
    case "square":
        int area = side * side;
        break;
    case "rectangle":
        int area = width * height;   // error CS0128: A local variable or function named 'area' is already defined in this scope
        break;
}

Bọc phần thân của mỗi case trong dấu ngoặc nhọn để nó có phạm vi riêng: case "square": { int area = side * side; ... break; }.

break bên trong vòng lặp

break trong một case thoát khỏi switch, không phải vòng lặp bao quanh. continue thì ngược lại: switch không phải vòng lặp, nên continue đi tới lần lặp tiếp theo của vòng lặp.

Đầu ra:

end marker found
Total: 134

Vòng lặp vẫn tiếp tục sau "end" và cộng thêm 99. Để dừng vòng lặp từ bên trong switch, hãy đặt một cờ rồi kiểm tra nó sau switch, hoặc chuyển vòng lặp vào một phương thức và dùng return. Trang về break và continue so sánh các lựa chọn này.

Nhãn case dạng pattern và when

Từ C# 7.0, một nhãn case có thể là một pattern thay vì hằng số: nó có thể kiểm tra kiểu của giá trị và thêm điều kiện when. Dạng này phổ biến trong code C# hiện đại:

static string Describe(object value)
{
    switch (value)
    {
        case null:
            return "nothing";
        case int n when n < 0:
            return $"negative int {n}";
        case int n:
            return $"int {n}";
        case string s when s.Length == 0:
            return "empty string";
        case string s:
            return $"string \"{s}\"";
        default:
            return $"other: {value.GetType().Name}";
    }
}

Với pattern, thứ tự rất quan trọng: case khớp đầu tiên sẽ thắng, và trình biên dịch báo lỗi khi một case không bao giờ được tới vì case trước đã bao phủ nó (đặt case int n: trên case int n when n < 0: sẽ lỗi). Mệnh đề when cũng dùng được với case hằng số: case 0 when isAdmin:. C# 9 bổ sung pattern so sánh và pattern logic, nên một case có thể viết case >= 90: hoặc case 'a' or 'e' or 'i':.

Trên C# 7 không dùng pattern, cùng phép phân loại theo kiểu đó là một chuỗi if với type pattern is:

Đầu ra:

negative int -3
int 12
empty string
string "hi"
other: Decimal
nothing

Khi mọi nhánh của switch chỉ tính ra một giá trị, switch expression còn ngắn hơn nữa: var grade = score switch { >= 90 => "A", >= 80 => "B", _ => "C" };. Trang về switch expression trình bày cú pháp, pattern và quy tắc về tính đầy đủ của nó.

Có thể switch trên những gì

Kiểu giá trịCase hằng sốGhi chú
int, long, byte và các kiểu số nguyên kháccótrường hợp kinh điển
charcócase 'a':
stringcóordinal, phân biệt hoa thường, null đi vào default
boolcóhợp lệ, nhưng if rõ ràng hơn
mọi enumcókhông kiểm tra tính đầy đủ
double, decimaltừ C# 7chỉ hằng số; khoảng giá trị cần pattern so sánh của C# 9
mọi kiểu kháctừ C# 7thông qua type pattern, case Circle c:

Câu hỏi thường gặp

Có bắt buộc dùng break trong switch của C# không?

Mỗi phần case phải kết thúc bằng một câu lệnh thoát khỏi nó: thường là break, nhưng return, throw, continue (bên trong vòng lặp) hoặc goto case cũng được tính. Chạy hết một phần rồi rơi xuống phần kế tiếp là lỗi biên dịch, CS0163 Control cannot fall through from one case label ('case 1:') to another, và quy tắc này áp dụng cả với phần cuối cùng.

Làm sao xử lý nhiều giá trị trong một case của switch C#?

Xếp chồng các nhãn và không đặt gì giữa chúng: case "sat": case "sun": Console.WriteLine("weekend"); break;. Nhãn case rỗng được phép rơi xuống nhãn kế tiếp; chỉ phần nào có câu lệnh mới phải kết thúc bằng break. Từ C# 9 trở đi, một case dạng pattern còn có thể viết case 1 or 2 or 3:.

Switch trên chuỗi trong C# có phân biệt hoa thường không?

Có. Các case chuỗi được so sánh bằng phép so sánh ordinal có phân biệt hoa thường, nên "Yes" không khớp với case "yes":. Hãy chuẩn hóa giá trị trước bằng switch (input.Trim().ToLowerInvariant()). Một chuỗi null không khớp case hằng số nào và đi vào default.

Switch trong C# có fall through như trong C hay Java không?

Không ngầm định. Khi bạn muốn một case chạy tiếp sang case khác, hãy viết rõ bằng goto case <value>; hoặc goto default;. Cách này khiến mọi lần fall through đều hiện rõ trong code, đó là lý do C# cấm kiểu fall through lặng lẽ.

Câu lệnh switch và switch expression trong C# khác nhau thế nào?

Câu lệnh switch chạy các câu lệnh và cần case, break và default. Switch expression (từ C# 8 trở đi) tạo ra một giá trị: var label = n switch { 0 => "zero", > 0 => "positive", _ => "negative" };. Dùng biểu thức khi mọi nhánh chỉ tính ra một kết quả.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu