Menu

Enum C#: Value, ToString, Parse, Flags, dan Iterasi

Cara kerja enum di C#: mendeklarasikan konstanta bernama, nilai integer di baliknya dan casting, mengubah enum menjadi string dan string menjadi enum dengan Parse dan TryParse, menampilkan semua nilainya, [Flags] dengan operator bitwise dan HasFlag, switch pada enum, dan menangani nilai yang tidak terdefinisi.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

Enum (enumerasi) adalah tipe yang nilainya berupa sekumpulan konstanta bernama yang tetap: status pesanan, hari dalam seminggu, level log. Di baliknya, setiap nama adalah sebuah integer, tetapi sistem tipe mencegah OrderStatus tercampur dengan int biasa atau dengan enum lain.

Mendeklarasikan dan memakai enum

Tuliskan nama-nama member-nya di dalam kurung kurawal. Secara default, yang pertama bernilai 0 dan setiap yang berikutnya bertambah satu:

Output:

Paid
On its way
True
2

Enum adalah tipe sungguhan: method yang menerima OrderStatus tidak bisa dipanggil dengan 3 atau dengan LogLevel secara tidak sengaja. Enum adalah tipe value, jadi tidak pernah null dan dibandingkan dengan == berdasarkan nilainya.

Nilai eksplisit dan tipe di baliknya

Kamu bisa menentukan angkanya sendiri. Hal ini penting setiap kali angkanya keluar dari programmu (kolom database, status HTTP, format file), karena mengubah penomoran akan merusak data yang sudah tersimpan:

Output:

404
Created
418
1
Byte

Ada dua hal yang perlu diperhatikan. Cast dari int ke enum tidak pernah gagal: (HttpStatus)418 adalah nilai yang valid tetapi tidak punya nama, dan dicetak sebagai angka. Lalu tipe di baliknya bisa berupa tipe integral apa pun (byte, short, long, ...), yang hanya penting untuk kode yang peka terhadap ukuran penyimpanan; int adalah default-nya dan hampir selalu menjadi pilihan yang tepat.

Saat kamu menambahkan member belakangan, tambahkan di akhir atau beri nilai eksplisit. Menyisipkan Refunded di antara Paid dan Shipped diam-diam mengubah angka setiap member setelahnya.

Enum ke string

ToString() mengembalikan nama member-nya, yang juga dipakai oleh Console.WriteLine dan string interpolation. Format string mengubah output-nya:

Output:

Warning
2
00000002
[Warning]
Error
Error
Needs attention

Nama member adalah identifier, jadi tidak boleh berisi spasi dan tidak diterjemahkan. Untuk teks yang ditampilkan ke pengguna, petakan nilainya sendiri, seperti yang dilakukan Label, atau dengan Dictionary<LogLevel, string>. Sebagian codebase menaruh attribute [Description("Needs attention")] pada setiap member lalu membacanya dengan reflection; halaman reflection dan attribute menunjukkan cara kerja pencarian itu.

String ke enum: Parse dan TryParse

Enum.Parse mengubah nama kembali menjadi nilai dan melempar ArgumentException kalau tidak ada yang cocok. Enum.TryParse mengembalikan false sebagai gantinya, dan itulah yang kamu inginkan untuk input apa pun yang tidak kamu kendalikan:

Output:

Large
Medium
Parse threw ArgumentException for Huge
small  parsed=True  value=Small  defined=True
XL     parsed=False value=Small  defined=True
2      parsed=True  value=Large  defined=True
7      parsed=True  value=7      defined=False

Dua baris terakhir adalah jebakannya. Kedua method menerima string berupa angka, jadi "7" berhasil di-parse menjadi Size yang tidak punya nama. Selain itu, TryParse yang gagal mengisi hasilnya dengan 0, yang di sini adalah Small yang terlihat valid. Saat teksnya berasal dari query string, file konfigurasi, atau form, selalu cek nilai kembaliannya sekaligus Enum.IsDefined:

if (Enum.TryParse(input, true, out Size size) && Enum.IsDefined(typeof(Size), size))
{
    // safe to use size
}

.NET Core 2.0 ke atas menambahkan Enum.Parse<Size>("Large") versi generic yang tidak butuh cast.

Menampilkan semua nilai

Enum.GetValues mengembalikan setiap member, diurutkan berdasarkan nilai numeriknya (dibandingkan sebagai unsigned, jadi member negatif ada di akhir); Enum.GetNames mengembalikan nama-namanya. Begitulah cara kamu mengisi dropdown atau memvalidasi terhadap setiap pilihan:

Output:

Free       0 EUR/month
Starter    9 EUR/month
Pro       29 EUR/month
Team      99 EUR/month
Free | Starter | Pro | Team
3 paid plans

Enum.GetValues(typeof(Plan)) mengembalikan Array biasa, itulah sebabnya ada Cast<Plan>() sebelum LINQ. Di .NET 5 ke atas, Enum.GetValues<Plan>() langsung mengembalikan Plan[] yang bertipe.

Flags: menggabungkan nilai

Sebagian enum menggambarkan sekumpulan pilihan, bukan satu pilihan: izin file, hari buka sebuah toko, saluran notifikasi. Beri setiap member bit-nya sendiri (1, 2, 4, 8, ...), tambahkan None = 0, dan tandai enum-nya dengan [Flags]. Nilainya lalu bisa digabungkan dengan |:

Output:

Read, Share
Editor, Share
True
False
Editor
3
Read, Delete
True

Fungsi setiap operator: | menyalakan bit, & ~X mematikannya, ^ membaliknya, dan (value & X) != 0 atau value.HasFlag(X) mengujinya. HasFlag(X) berarti "semua bit milik X menyala", jadi HasFlag(None) bernilai true untuk setiap nilai, dan HasFlag(Editor) membutuhkan Read dan Write sekaligus.

Perhatikan baris kedua: saat sebuah gabungan bernama mencakup sebagian bit yang menyala, ToString memakainya, jadi Read | Write | Share dicetak sebagai Editor, Share. Ingat hal ini sebelum mem-parse output ToString dengan cara selain Enum.Parse.

Attribute ini tidak mengubah aritmetikanya. Yang diubahnya adalah format: tanpa [Flags], Read | Share dicetak sebagai 9, karena tidak ada satu member pun yang punya nilai itu. Dengan attribute ini, ToString dan Parse sama-sama bekerja dengan bentuk yang dipisahkan koma. Member tetap harus berupa pangkat dua; menulis Read, Write, Delete dengan penomoran default (0, 1, 2) membuat Write | Delete sama dengan 3, nilai yang tidak bermakna.

Switch pada enum

switch adalah cara yang wajar untuk bertindak berdasarkan enum. Sertakan cabang default, karena variabel enum bisa menyimpan nilai tanpa nama:

switch (status)
{
    case OrderStatus.Pending:
    case OrderStatus.Paid:
        return "Preparing";
    case OrderStatus.Shipped:
        return "On the way";
    case OrderStatus.Delivered:
        return "Delivered";
    default:
        return "Unknown";
}

Sejak C# 8, switch expression lebih singkat. Tanpa cabang _, compiler memberi warning: CS8509 saat ada member bernama yang terlewat, dan CS8524 saat semua nama sudah ditangani tetapi nilai tanpa nama seperti (OrderStatus)7 belum:

string text = status switch
{
    OrderStatus.Pending or OrderStatus.Paid => "Preparing",   // 'or' pattern: C# 9
    OrderStatus.Shipped => "On the way",
    OrderStatus.Delivered => "Delivered",
    OrderStatus.Cancelled => "Cancelled",
    _ => throw new ArgumentOutOfRangeException(nameof(status)),
};

Nilai default dan nilai yang tidak terdefinisi

Nilai default setiap enum adalah 0, terlepas dari ada atau tidaknya member dengan nilai itu. Field, elemen array, dan TryParse yang gagal semuanya menghasilkan nilai ini. Rancang dengan memperhitungkan hal itu:

  • Jadikan 0 sebagai member "belum diatur" yang bermakna (None, Unknown), bukan pilihan sungguhan. Kalau tidak, field yang belum diinisialisasi diam-diam terbaca sebagai pilihan sungguhan yang pertama.
  • Validasi angka dari luar dengan Enum.IsDefined. Untuk enum [Flags], IsDefined mengembalikan false untuk gabungan yang tidak punya nama (Read | Share), jadi periksa bit-nya: (value & ~Permissions.All) == 0 dengan member All yang mencakup setiap bit.

Kesalahan umum

  • Hanya mengandalkan TryParse. String berupa angka ikut ter-parse, dan parse yang gagal menghasilkan 0. Tambahkan Enum.IsDefined.
  • Mengandalkan penomoran implisit untuk nilai yang disimpan. Menyisipkan member mengubah nomor member setelahnya. Beri nilai eksplisit pada setiap enum yang disimpan.
  • Flags tanpa pangkat dua. Penomoran default (0, 1, 2, 3) membuat bit saling tumpang tindih. Pakai 1, 2, 4, 8, atau 1 << n.
  • Menampilkan ToString() ke pengguna. Nama member adalah identifier kode. Petakan nilainya ke teks tampilan.
  • Tidak ada default di switch. Enum bisa menyimpan nilai di luar member bernamanya.

Pertanyaan yang sering diajukan

Bagaimana cara mengubah enum menjadi string di C#?

Panggil ToString(): OrderStatus.Shipped.ToString() mengembalikan "Shipped", dan string interpolation melakukan hal yang sama. ToString("D") memberi angkanya. Untuk nama yang sudah diketahui saat compile, nameof(OrderStatus.Shipped) adalah konstanta. Untuk teks yang ditampilkan ke pengguna, dengan spasi atau terjemahan, petakan value ke string sendiri (dengan switch atau dictionary) alih-alih mengandalkan nama member-nya.

Bagaimana cara mengubah string menjadi enum di C#?

Pakai Enum.TryParse<OrderStatus>(text, true, out var status), yang mengembalikan false alih-alih melempar exception saat teksnya tidak cocok dengan member mana pun (true membuatnya tidak peka huruf besar-kecil). Enum.Parse(typeof(OrderStatus), text) melempar ArgumentException untuk input yang salah. Keduanya juga menerima string berupa angka seperti "42", jadi cek hasilnya dengan Enum.IsDefined saat input-nya berasal dari pengguna.

Bagaimana cara mengonversi antara enum dan int di C#?

Lakukan cast ke dua arah: int code = (int)OrderStatus.Paid; dan var status = (OrderStatus)2;. Cast dari int tidak pernah gagal, bahkan untuk angka yang tidak punya member yang cocok; hasilnya adalah nilai enum yang dicetak sebagai angka. Validasi dengan Enum.IsDefined(typeof(OrderStatus), value) saat angkanya berasal dari luar.

Bagaimana cara melakukan perulangan pada semua nilai enum di C#?

foreach (OrderStatus s in Enum.GetValues(typeof(OrderStatus))) mengunjungi setiap member sesuai urutan nilai numeriknya. Sejak .NET 5 ada versi generic-nya, Enum.GetValues<OrderStatus>(), yang tidak butuh cast. Enum.GetNames(typeof(OrderStatus)) mengembalikan nama-namanya sebagai string.

Apa fungsi [Flags] pada enum C#?

Attribute ini menandai enum yang nilainya berupa bit yang dimaksudkan untuk digabungkan dengan |, seperti Read | Write. Beri setiap member nilai pangkat dua (1, 2, 4, 8) dan sebuah None = 0. Attribute ini membuat ToString() mencetak gabungannya sebagai "Read, Write" dan membuat Enum.Parse bisa membaca kembali format itu. Uji sebuah bit dengan HasFlag atau (value & Permissions.Write) != 0.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai