Haruskah Enum dimulai dengan 0 atau 1?

138

Bayangkan saya telah mendefinisikan Enum berikut:

public enum Status : byte
{
    Inactive = 1,
    Active = 2,
}

Apa praktik terbaik untuk menggunakan enum? Haruskah dimulai dengan 1seperti contoh di atas atau dimulai dengan 0(tanpa nilai eksplisit) seperti ini:

public enum Status : byte
{
    Inactive,
    Active
}
Acaz Souza
sumber
13
Apakah Anda benar-benar perlu menomori mereka secara eksplisit?
Yuck
165
Enum dibuat hanya agar hal-hal seperti ini tidak penting.
BoltClock
9
@Daniel - arrgh tidak! Lebih baik menggunakan enum saat menurut Anda boolean akan berhasil daripada menggunakan boolean saat Anda memikirkan enum.
AAT
22
@Daniel karena nilai FileNotFound, tentu saja
Joubarc
5
xkcd.com/163 berlaku untuk enum bahkan lebih baik daripada yang diterapkan pada indeks larik.
berangkat sekitar

Jawaban:

164

Pedoman Desain Kerangka :

✔️ JANGAN berikan nilai nol pada enum sederhana.

Pertimbangkan untuk menyebut nilai seperti "Tidak ada". Jika nilai seperti itu tidak sesuai untuk enum khusus ini, nilai default paling umum untuk enum harus diberi nilai dasar nol.

Panduan Desain Kerangka / Desain Bendera Enums :

❌ HINDARI penggunaan nilai enum flag nol kecuali nilai tersebut mewakili "semua flag dihapus" dan dinamai dengan tepat, seperti yang ditentukan oleh pedoman berikutnya.

✔️ JANGAN beri nama nilai nol dari enum bendera Tidak ada. Untuk enum tanda, nilainya harus selalu berarti "semua tanda dihapus".

Andrey Taptunov
sumber
28
Gagal lebih awal: Jika 'Tidak Ada' tidak sesuai tetapi tidak ada nilai default logis, saya akan tetap memberi nilai nol (dan menyebutnya 'Tidak Ada' atau 'Tidak Valid') yang tidak dimaksudkan untuk digunakan, hanya agar jika anggota kelas dari pencacahan itu tidak diinisialisasi dengan benar, nilai yang tidak diinisialisasi dapat dengan mudah terlihat, dan dalam switchpernyataan itu akan melompat ke defaultbagian, di mana saya melempar InvalidEnumArgumentException. Jika tidak, sebuah program mungkin secara tidak sengaja terus berjalan dengan nilai nol dari pencacahan, yang mungkin valid dan tidak diperhatikan.
Allon Guralnek
1
@Allon Tampaknya lebih baik untuk hanya memberikan nilai enum yang Anda tahu valid dan kemudian memeriksa nilai yang tidak valid di penyetel dan / atau konstruktor. Dengan cara itu Anda langsung tahu jika beberapa kode tidak berfungsi dengan benar daripada membiarkan objek dengan data tidak valid ada untuk beberapa waktu yang tidak diketahui dan mencari tahu tentangnya nanti. Kecuali 'Tidak Ada' mewakili status yang valid, Anda tidak boleh menggunakannya.
wprl
@SoloBold: Kedengarannya seperti kasus di mana Anda tidak lupa menginisialisasi anggota kelas enum dalam konstruktor. Jumlah validasi tidak akan membantu Anda jika Anda lupa menginisialisasi atau lupa memvalidasi. Juga, ada kelas DTO sederhana yang tidak memiliki konstruktor, tetapi mengandalkan penginisialisasi objek sebagai gantinya. Memburu serangga seperti itu bisa sangat menyakitkan. Namun demikian, menambahkan nilai enumerasi yang tidak terpakai membuat API menjadi jelek. Saya akan menghindarinya karena API yang ditujukan untuk konsumsi publik.
Allon Guralnek
@ Allon Itu poin yang bagus, tapi saya berpendapat bahwa Anda harus memvalidasi enum dalam fungsi penyetel dan membuang dari pengambil jika penyetel tidak pernah dipanggil. Dengan cara itu Anda memiliki satu titik kegagalan dan Anda dapat merencanakan di lapangan selalu memiliki nilai yang valid, yang menyederhanakan desain dan kode. Lagipula dua sen saya.
wprl
@SoloBold: Bayangkan sebuah kelas DTO dengan 15 properti yang diimplementasikan secara otomatis. Tubuhnya memiliki panjang 15 baris. Sekarang bayangkan kelas yang sama dengan properti reguler. Itu minimal 180 baris sebelum menambahkan logika verifikasi apa pun. Kelas ini hanya digunakan secara internal untuk tujuan transfer data saja. Mana yang lebih Anda pilih, kelas 15 baris atau kelas 180+ baris? Kesederhanaan memiliki nilainya. Tapi tetap saja, kedua gaya kami benar, keduanya berbeda. (Saya kira di sinilah AOP menyapu dan memenangkan kedua sisi argumen).
Allon Guralnek
67

Yah, saya rasa saya tidak setuju dengan sebagian besar jawaban yang mengatakan tidak secara eksplisit menomori mereka. Saya selalu secara eksplisit memberi nomor mereka, tetapi itu karena dalam banyak kasus saya akhirnya menahan mereka dalam aliran data di mana mereka disimpan sebagai nilai integer. Jika Anda tidak secara eksplisit menambahkan nilai dan kemudian menambahkan nilai baru Anda dapat menghentikan serialisasi dan kemudian tidak dapat secara akurat memuat objek lama yang ada. Jika Anda akan melakukan semua jenis penyimpanan persisten dari nilai-nilai ini maka saya akan sangat merekomendasikan pengaturan nilai secara eksplisit.

pstrjds.dll
sumber
9
+1, disetujui, tetapi hanya dalam kasus di mana kode Anda bergantung pada bilangan bulat untuk beberapa alasan eksternal (misalnya serialisasi). Di tempat lain Anda harus tetap membiarkan kerangka melakukan tugasnya. Jika Anda mengandalkan nilai integer secara internal, maka Anda mungkin melakukan sesuatu yang salah (lihat: biarkan framework melakukan tugasnya).
Matthew Scharley
3
Saya suka mempertahankan teks enum. Membuat database imo jauh lebih berguna.
Dave
2
Biasanya, Anda tidak perlu mengatur nilainya secara eksplisit ... bahkan ketika nilainya berseri. cukup SELALU tambahkan nilai baru di akhir. ini akan menyelesaikan masalah serialisasi. jika tidak, Anda mungkin memerlukan versi penyimpanan data Anda (mis. header file yang berisi versi untuk mengubah perilaku saat membaca / menulis nilai) (atau lihat pola kenang-kenangan)
Beachwalker
4
@ Dave: Kecuali jika Anda secara eksplisit mendokumentasikan bahwa teks enum itu suci, Anda membuat diri Anda gagal jika programmer masa depan memutuskan untuk menyesuaikan nama agar lebih jelas atau sesuai dengan beberapa konvensi penamaan.
supercat
1
@pstrjds: ini adalah pertukaran gaya, saya kira - ruang disk murah, waktu yang dihabiskan terus-menerus mengubah antara nilai enum dan int ketika mencari db relatif mahal (jadi dua kali lipat jika Anda memiliki pengaturan alat pelaporan terhadap database atau yang serupa) . Jika Anda khawatir tentang ruang, dengan versi server SQL yang lebih baru Anda dapat memiliki database yang dikompresi, yang berarti 1000 kejadian "SomeEnumTextualValue" akan menggunakan hampir tidak ada lagi ruang. Tentu saja ini tidak akan berhasil untuk semua proyek - ini trade off. Saya rasa kekhawatiran tentang bandwidth berbau seperti optimasi prematur, mungkin!
Dave
15

Enum adalah tipe nilai dan nilai defaultnya (misalnya untuk bidang Enum di kelas) akan menjadi 0 jika tidak diinisialisasi secara eksplisit.

Oleh karena itu Anda biasanya ingin memiliki 0 sebagai konstanta yang ditentukan (mis. Tidak Diketahui).

Dalam contoh Anda, jika Anda ingin Inactivemenjadi default, maka nilainya harus nol. Jika tidak, Anda mungkin ingin mempertimbangkan untuk menambahkan konstantaUnknown .

Beberapa orang merekomendasikan agar Anda tidak secara eksplisit menentukan nilai konstanta Anda. Mungkin nasihat yang bagus dalam banyak kasus, tetapi ada beberapa kasus ketika Anda ingin melakukannya:

  • Tandai enum

  • Enum yang nilainya digunakan dalam interop dengan sistem eksternal (misalnya COM).

Joe
sumber
Saya telah menemukan bahwa enum flags jauh lebih mudah dibaca ketika Anda tidak secara eksplisit mengatur nilainya. - Juga jauh lebih sedikit rawan kesalahan untuk membiarkan kompilator melakukan matematika biner untuk Anda. (Yaitu [Flags] enum MyFlags { None = 0, A, B, Both = A | B, /* etc. */ }jauh lebih mudah dibaca, daripada [Flags] enum MyFlags { None = 0, A = 1, B = 2, Both = 3, /* etc */ }.)
BrainSlugs83
1
@ BrainSlugs83 - Saya tidak melihat bagaimana itu akan membantu dalam kasus umum - misalnya [Flags] enum MyFlags { None=0, A, B, C } akan menghasilkan [Flags] enum MyFlags { None=0, A=1, B=2, C=3 }, sedangkan untuk enum Flags Anda biasanya ingin C = 4.
Joe
14

Kecuali Anda memiliki alasan khusus untuk mengubahnya, biarkan enum dengan nilai defaultnya, yang dimulai dari nol.

public enum Status : byte
{
    Inactive,
    Active
}
IkanBasketGordo
sumber
6

Saya akan mengatakan praktik terbaik adalah tidak menomori mereka dan membiarkannya tersirat - yang akan dimulai dari 0. Sejak implisit, preferensi bahasa yang selalu baik untuk diikuti :)

John Humphreys - w00te
sumber
6

Saya akan memulai enum tipe boolean dengan 0.

Kecuali "Inative" berarti sesuatu selain "Inactive" :)

Ini mempertahankan standar untuk mereka.

Mark Schultheiss
sumber
6

Saya akan mengatakan, itu tergantung pada bagaimana Anda menggunakannya. Untuk menandai enum, praktik yang baik adalah menggunakan 0 untuk Nonenilai, seperti itu:

[Flags]
enum MyEnum
{
    None = 0,
    Option1 = 1,
    Option2 = 2,
    Option3 = 4,
    All = Option1 | Option2 | Option3,
}

Ketika enum Anda kemungkinan besar akan dipetakan ke tabel pencarian database, saya akan memulainya dengan 1. Seharusnya tidak terlalu penting untuk kode yang ditulis secara profesional, tetapi ini meningkatkan keterbacaan.

Dalam kasus lain saya akan membiarkannya apa adanya, tidak peduli apakah mereka mulai dengan 0 atau 1.

Michael Sagalovich
sumber
6

Kecuali Anda memiliki alasan kuat untuk menggunakan nilai mentah, Anda sebaiknya hanya menggunakan nilai implisit dan mereferensikannya Status.Active danStatus.Inactive .

Tangkapannya adalah Anda mungkin ingin menyimpan data dalam file datar atau DB, atau menggunakan file datar atau DB yang dibuat orang lain. Jika Anda membuatnya sendiri, buatlah penomorannya sesuai dengan kegunaan Enum.

Jika datanya bukan milik Anda, tentu saja Anda akan ingin menggunakan apa pun yang digunakan pengembang asli sebagai skema penomoran.

Jika Anda berencana menggunakan Enum sebagai satu set flag, ada konvensi sederhana yang layak untuk diikuti:

enum Example
{
  None      = 0,            //  0
  Alpha     = 1 << 0,       //  1
  Beta      = 1 << 1,       //  2
  Gamma     = 1 << 2,       //  4
  Delta     = 1 << 3,       //  8
  Epsilon   = 1 << 4,       // 16
  All       = ~0,           // -1
  AlphaBeta = Alpha | Beta, //  3
}

Nilai harus merupakan pangkat dua dan dapat diekspresikan menggunakan operasi bit-shift. None, jelas harus 0, tetapi Allkurang jelas -1. ~0adalah negasi biner dari 0dan menghasilkan angka yang setiap bitnya disetel 1, yang mewakili nilai-1 . Untuk tanda gabungan (sering digunakan untuk kenyamanan) nilai lain dapat digabungkan menggunakan bitwise atau operator |.

zzzzBov
sumber
3

Jangan tetapkan nomor apa pun. Gunakan saja seperti yang seharusnya digunakan.

Hooch
sumber
3

Jika tidak ditentukan penomoran dimulai dari 0.

Penting untuk eksplisit karena enum sering berseri dan disimpan sebagai int, bukan string.

Untuk enum apa pun yang disimpan dalam database, kami selalu secara eksplisit memberi nomor opsi untuk mencegah pengalihan dan penugasan ulang selama pemeliharaan.

Menurut Microsoft, konvensi yang disarankan adalah menggunakan opsi nol pertama untuk mewakili nilai default yang tidak diinisialisasi atau paling umum.

Di bawah ini adalah jalan pintas untuk memulai penomoran dari 1, bukan 0.

public enum Status : byte
{
    Inactive = 1,
    Active
}

Jika Anda ingin menyetel nilai bendera untuk menggunakan operator bit pada nilai enum, jangan mulai penomoran pada nilai nol.

dru
sumber
2

Jika Anda mulai dari 1, maka Anda dapat dengan mudah menghitung barang-barang Anda.

{
    BOX_THING1     = 1,
    BOX_THING2     = 2,
    BOX_NUM_THING  = BOX_THING2
};

Jika Anda mulai dari 0, gunakan nilai pertama sebagai nilai untuk hal-hal yang belum diinisialisasi.

{
    BOX_NO_THING   = 0,
    BOX_THING1     = 1,
    BOX_THING2     = 2,
    BOX_NUM_THING  = BOX_THING2
};
Jonathan Cline IEEE
sumber
5
Maaf, Jonathan. Saya pikir, saran ini adalah sedikit "sekolah tua" dalam pikiran saya (jenis konvensi berasal dari tahun-tahun tingkat rendah). Ini tidak masalah sebagai solusi cepat untuk "menyematkan" beberapa info tambahan tentang enum tetapi ini bukan praktik yang baik di sistem yang lebih besar. Anda tidak boleh menggunakan enum jika Anda membutuhkan info tentang jumlah nilai yang tersedia, dll. Dan bagaimana dengan BOX_NO_THING1? Apakah Anda akan memberinya BOX_NO_THING + 1? Enum harus digunakan sebagaimana mestinya untuk digunakan: Nilai (int) spesifik yang diwakili oleh nama "berbicara".
Beachwalker
Hm. Anda berasumsi itu jadul karena saya menggunakan huruf besar semua, saya kira, daripada MicrosoftBumpyCaseWithLongNames. Meskipun saya setuju lebih baik menggunakan iterator daripada loop sampai mencapai definisi XyzNumDefsInMyEnum enum'ed.
Jonathan Cline IEEE
Ini adalah praktik yang buruk di C # dalam berbagai cara. Sekarang saat Anda menghitung enum dengan cara yang benar, atau jika Anda mencoba menghitungnya dengan cara yang benar, Anda akan mendapatkan objek duplikat ekstra. Juga itu membuat panggilan .ToString () berpotensi ambigu (mengacaukan serialisasi modern), antara lain.
BrainSlugs83
0

Pertama-tama, kecuali Anda menentukan nilai tertentu karena suatu alasan (nilai numerik memiliki arti di tempat lain, misalnya Database atau layanan eksternal) maka jangan tentukan nilai numerik sama sekali dan biarkan nilai tersebut eksplisit.

Kedua, Anda harus selalu memiliki item bernilai nol (dalam enum non-flags). Elemen itu akan digunakan sebagai nilai default.

Justin Niessner
sumber
0

Jangan memulainya dari 0 kecuali ada alasan untuk itu, seperti menggunakannya sebagai indeks untuk larik atau daftar, atau jika ada alasan praktis lainnya (seperti menggunakannya dalam operasi bitwise).

Anda enumharus mulai tepat di tempat yang diperlukan. Ini juga tidak perlu berurutan. Nilai, jika ditetapkan secara eksplisit, perlu mencerminkan beberapa makna semantik atau pertimbangan praktis. Misalnya, enum"botol di dinding" harus diberi nomor dari 1 hingga 99, sedangkan enumuntuk pangkat 4 mungkin harus dimulai dari 4 dan dilanjutkan dengan 16, 64, 256, dll.

Selain itu, menambahkan elemen bernilai nol ke dalam enumseharusnya hanya dilakukan jika itu mewakili status yang valid. Kadang-kadang "tidak ada", "tidak diketahui", "hilang", dll. Adalah nilai yang valid, tetapi seringkali tidak.

wprl
sumber
-1

Saya suka memulai enum saya dari 0, karena itu defaultnya, tapi saya juga suka memasukkan nilai Unknown, dengan nilai -1. Ini kemudian menjadi default dan kadang-kadang dapat membantu debugging.

Tomas McGuiness
sumber
4
Ide yang mengerikan. Sebagai tipe nilai, enum selalu diinisialisasi ke nol. Jika Anda akan memiliki beberapa nilai yang mewakili tidak diketahui atau tidak diinisialisasi, nilainya harus 0. Anda tidak dapat mengubah default ke -1, pengisian nol dikodekan secara keras di seluruh CLR.
Ben Voigt
Ah, saya tidak menyadarinya. Saya biasanya menetapkan nilai enum value / property ketika saya decalre / initialise. Terima kasih atas penunjuknya.
Tomas McGuinness