Ekspresi switch memetakan sebuah nilai input ke sebuah hasil. Fitur ini ditambahkan di C# 8 dan menggantikan pola umum berupa pernyataan switch yang setiap case-nya hanya mengembalikan atau mengisi sesuatu:
// C# 8 and later
string StatusText(int code) => code switch
{
200 => "OK",
301 or 302 => "Redirect", // 'or' needs C# 9
404 => "Not Found",
>= 500 => "Server Error", // relational patterns need C# 9
_ => "Unknown"
};
Bacalah dari atas ke bawah: nilai sebelum switch diuji terhadap setiap cabang secara berurutan, dan cabang pertama yang polanya cocok memberikan hasilnya. Tidak ada kata kunci case, break, atau default; _ (pola discard) cocok dengan apa pun dan menjadi cadangannya.
Di C# 7, pemetaan yang sama adalah pernyataan switch di dalam method kecil, dengan return di setiap case:
Output:
200 OK
302 Redirect
404 Not Found
503 Server Error
418 Unknown
Bentuk ekspresi lebih pendek terutama karena setiap cabang hanya satu baris dan body method-nya hilang. Bentuk ini juga merupakan ekspresi, jadi bisa ditaruh di mana pun sebuah nilai bisa ditaruh: argumen, lubang interpolation, return, atau initializer field.
Aturan sintaks
var result = input switch
{
pattern1 => value1,
pattern2 when condition => value2,
_ => fallback
};
- Input-nya ditulis lebih dulu, lalu
switch. Ini kebalikan dari bentuk pernyataan. - Setiap cabang berbentuk
pattern => expression. Sisi kanannya harus satu ekspresi; untuk logika yang lebih banyak, panggil sebuah method. - Cabang-cabangnya dipisahkan koma. Koma di belakang cabang terakhir diizinkan.
- Semuanya adalah satu ekspresi, jadi pernyataan yang memakainya diakhiri
;setelah kurung kurawal penutup. - Semua cabang harus menghasilkan nilai dengan tipe yang sama.
var x = n switch { 0 => "zero", _ => 0 };tidak bisa dikompilasi (CS8506, tidak ada tipe terbaik). Sejak C# 9, tipe target yang dideklarasikan menyelesaikan masalah ini:object x = n switch { 0 => "zero", _ => 0 };bisa dikompilasi. - Setiap cabang harus menghasilkan nilai, jadi sebuah cabang tidak bisa memanggil method
voidsepertiConsole.WriteLine, dan ekspresi switch tidak bisa berdiri sendiri sebagai pernyataan (CS0201). Untuk aksi, pakai pernyataan switch. - Sebuah cabang boleh melempar exception alih-alih menghasilkan nilai:
_ => throw new ArgumentOutOfRangeException(nameof(input)). - Compiler menolak cabang yang tidak akan pernah tercapai karena sudah dicakup cabang sebelumnya, dengan error CS8510,
The pattern is unreachable. It has already been handled by a previous arm of the switch expression or it is impossible to match.Taruh cabang yang spesifik sebelum yang umum.
Pola relasional dan logika (C# 9)
C# 9 menambahkan <, <=, >, >= sebagai pola, serta penggabung and, or, dan not. Dengan pola ini, tabel rentang terbaca seperti sebuah spesifikasi:
// C# 9 and later
string Grade(int score) => score switch
{
< 0 or > 100 => throw new ArgumentOutOfRangeException(nameof(score)),
>= 90 => "A",
>= 80 => "B",
>= 70 => "C",
>= 60 => "D",
_ => "F"
};
string AgeGroup(int age) => age switch
{
< 13 => "child",
>= 13 and <= 19 => "teen",
_ => "adult"
};
bool IsLetter(char c) => c is >= 'a' and <= 'z' or >= 'A' and <= 'Z';
Karena cabang dicoba secara berurutan, >= 80 hanya melihat nilai di bawah 90. Pola relasional hanya menerima konstanta di sisi kanannya; untuk membandingkan dengan sebuah variabel, pakai guard when. Padanan tabel rentang di C# 7 adalah rangkaian if:
Output:
95 -> A
83 -> B
71 -> C
64 -> D
12 -> F
not paling sering dipakai sebagai not null: x is not null, atau cabang not null => x.Name.
Pola property
Pola property mencocokkan member sebuah object: { Property: pattern, ... }. Jika digabung dengan ekspresi switch, pola ini bisa mengungkapkan aturan bisnis tanpa rangkaian kondisi &&:
// C# 8 and later
decimal ShippingCost(Order order) => order switch
{
{ Total: >= 100m } => 0m, // relational inside: C# 9
{ Country: "US", Express: true } => 15m,
{ Country: "US" } => 5m,
{ Express: true } => 30m,
null => throw new ArgumentNullException(nameof(order)),
_ => 12m
};
Setiap property yang disebutkan harus cocok agar cabangnya cocok. Pola property kosong { } cocok dengan nilai apa pun yang tidak null. C# 10 menambahkan extended property pattern untuk member bersarang: { Customer.Address.Country: "US" } alih-alih { Customer: { Address: { Country: "US" } } }.
Pola tuple
Switch pada tuple mencocokkan beberapa nilai sekaligus, dan inilah cara yang rapi untuk mengungkapkan sebuah tabel keputusan:
// C# 9 and later (the 'or' pattern)
string Winner(string a, string b) => (a, b) switch
{
("rock", "scissors") or ("scissors", "paper") or ("paper", "rock") => "player 1",
var (x, y) when x == y => "draw",
_ => "player 2"
};
var (x, y) membongkar tuple menjadi dua variabel yang bisa dibandingkan oleh guard when. Di C# 7, pengganti yang biasa dipakai adalah menggabungkan nilai-nilainya menjadi satu key, atau memakai dictionary berisi hasilnya:
Output:
player 1
player 2
draw
Pola tipe dan when
Sebuah cabang bisa menguji tipe runtime dan mengikat variabel, dan klausa when menambahkan kondisi apa pun yang tidak bisa diungkapkan lewat sintaks pola:
// C# 8 and later
double Area(Shape shape) => shape switch
{
Circle c => Math.PI * c.Radius * c.Radius,
Rectangle r when r.Width == r.Height => r.Width * r.Width,
Rectangle r => r.Width * r.Height,
null => throw new ArgumentNullException(nameof(shape)),
_ => throw new NotSupportedException(shape.GetType().Name)
};
Versi C# 7 memakai pola tipe is di dalam rangkaian if, yang mengikat variabel dengan cara yang sama:
Output:
Circle: 12.57
Rectangle: 13.50
Exhaustiveness dan SwitchExpressionException
Ekspresi switch harus menghasilkan nilai untuk setiap input. Jika compiler bisa melihat input yang tidak cocok dengan cabang mana pun, ia memberi peringatan:
warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '0' is not covered.
Ini peringatan, bukan error, jadi build kodenya tetap berhasil. Jika nilai yang tidak cocok datang saat program berjalan, ekspresi itu melempar System.Runtime.CompilerServices.SwitchExpressionException, yang pesannya diakhiri dengan nilai yang tidak cocok itu (Unmatched value was 3.).
Enum punya kasus khusus. Meskipun setiap member yang bernama sudah punya cabang, variabel enum bisa berisi bilangan bulat apa pun ((Size)3), jadi compiler melaporkan CS8524 untuk "nilai enum yang tidak bernama":
// C# 8 and later
enum Size { Small, Medium, Large }
string Code(Size s) => s switch
{
Size.Small => "S",
Size.Medium => "M",
Size.Large => "L",
// warning CS8524 without the next arm
_ => throw new ArgumentOutOfRangeException(nameof(s))
};
Tambahkan cabang _ eksplisit yang melempar exception yang berguna. Itu mendokumentasikan asumsinya, menghilangkan peringatan, dan memberi error yang lebih jelas daripada SwitchExpressionException jika suatu saat nilai yang salah muncul.
Dictionary sebagai alternatif
Jika cabang-cabangnya hanya berupa pasangan konstanta ke nilai dan tabelnya mungkin bertambah atau berasal dari konfigurasi, Dictionary melakukan pekerjaan yang sama di versi C# mana pun dan bisa dibangun saat program berjalan:
Output:
19.00
11.50
0
Negara yang tidak dikenal mendapat tarif 0m. Hasil perkalian decimal punya angka desimal sebanyak gabungan kedua operand-nya, itulah sebabnya 100m * 0.19m mencetak 19.00 dan tarif nol mencetak 0.
Ekspresi switch vs pernyataan switch
| pernyataan switch | ekspresi switch | |
|---|---|---|
| Tersedia sejak | C# 1 | C# 8 |
| Menghasilkan nilai | tidak | ya |
| Sintaks per cabang | case X: ... break; | X => value, |
| Cadangan | default: | _ => |
| Beberapa pernyataan per cabang | ya | tidak, panggil method |
| Peringatan exhaustiveness | tidak | ya (CS8509) |
| Tidak ada yang cocok saat berjalan | tidak ada yang dijalankan | SwitchExpressionException |
Kedua bentuk menerima pola yang sama (tipe, konstanta, property, tuple, relasional), jadi mempelajari satu bentuk sekaligus mengajarkan bentuk yang lain. Pilihannya soal bentuk: nilai lewat ekspresi, rangkaian aksi lewat pernyataan.
Pertanyaan yang sering diajukan
Apa itu ekspresi switch di C#?
Ekspresi switch, yang ditambahkan di C# 8, menghasilkan sebuah nilai: string text = code switch { 200 => "OK", 404 => "Not Found", _ => "Unknown" };. Input-nya ditulis sebelum kata kunci switch, setiap cabang berbentuk pattern => result, cabang-cabangnya dipisahkan koma, dan tidak ada kata kunci case, break, atau default.
Apa case default di ekspresi switch C#?
Pola discard _ cocok dengan apa pun, jadi _ => "Unknown" sebagai cabang terakhir berperan sebagai default. Tanpa cabang penangkap semua, compiler memberi peringatan (CS8509) jika ada input yang tidak tercakup, dan nilai yang tidak cocok saat program berjalan akan melempar SwitchExpressionException.
Bagaimana cara mencocokkan beberapa nilai dalam satu cabang ekspresi switch?
Pakai pola or dari C# 9: "sat" or "sun" => "weekend". Untuk rentang, gabungkan pola relasional dengan and: >= 13 and <= 19 => "teen". Di C# 8, kamu harus menuliskan nilai-nilainya sebagai cabang terpisah dengan hasil yang sama.
Bisakah memakai when di ekspresi switch C#?
Bisa. Sebuah cabang bisa menambahkan guard setelah polanya: Order o when o.Total > 100 => 0m. Cabang itu hanya cocok jika polanya cocok dan kondisi when bernilai true; jika tidak, evaluasi berlanjut ke cabang berikutnya.
Sebaiknya memakai ekspresi switch atau pernyataan switch?
Pakai ekspresi jika setiap cabang hanya menghasilkan nilai, misalnya memetakan status ke label atau menghitung harga. Pakai pernyataan jika cabangnya menjalankan beberapa pernyataan, punya efek samping, atau perlu keluar dari perulangan. Ekspresi juga memberimu peringatan exhaustiveness yang tidak diberikan oleh pernyataan.