Mengapa enum bendera biasanya ditentukan dengan nilai heksadesimal

122

Seringkali saya melihat deklarasi enum bendera yang menggunakan nilai heksadesimal. Sebagai contoh:

[Flags]
public enum MyEnum
{
    None  = 0x0,
    Flag1 = 0x1,
    Flag2 = 0x2,
    Flag3 = 0x4,
    Flag4 = 0x8,
    Flag5 = 0x10
}

Saat saya mendeklarasikan enum, saya biasanya mendeklarasikannya seperti ini:

[Flags]
public enum MyEnum
{
    None  = 0,
    Flag1 = 1,
    Flag2 = 2,
    Flag3 = 4,
    Flag4 = 8,
    Flag5 = 16
}

Adakah alasan atau alasan mengapa beberapa orang memilih untuk menulis nilai dalam heksadesimal daripada desimal? Cara saya melihatnya, lebih mudah menjadi bingung saat menggunakan nilai hex dan tidak sengaja menulis Flag5 = 0x16daripada Flag5 = 0x10.

Adi Lester
sumber
4
Apa yang membuat Anda lebih kecil kemungkinannya untuk menulis 10daripada 0x10jika Anda menggunakan angka desimal? Terutama karena ini adalah bilangan biner yang kita hadapi, dan hex dapat dikonversi secara mudah ke / dari biner? 0x111jauh lebih tidak menyebalkan untuk menerjemahkan di kepala seseorang daripada 273...
cHao
4
Sayang sekali C # tidak memiliki sintaks yang tidak secara eksplisit mengharuskan penulisan pangkat dua.
Kolonel Panic
Anda melakukan sesuatu yang tidak masuk akal di sini. Maksud di balik flag adalah bahwa mereka akan digabungkan dengan bitwise. Tetapi kombinasi bitwise bukanlah elemen dari jenisnya. Nilainya Flag1 | Flag23, dan 3 tidak sesuai dengan nilai domain apa pun MyEnum.
Kaz
Di mana Anda melihat itu? dengan reflektor?
giammin
@giammin Ini pertanyaan umum, bukan tentang implementasi khusus. Anda dapat mengambil proyek open source atau hanya kode yang tersedia di internet misalnya.
Adi Lester

Jawaban:

183

Alasan mungkin berbeda, tetapi keuntungan yang saya lihat adalah bahwa heksadesimal mengingatkan Anda: "Oke, kita tidak lagi berurusan dengan angka di dunia basis sepuluh yang diciptakan manusia secara sembarangan. Kita berurusan dengan bit - dunia mesin - dan kita akan bermain sesuai aturannya. " Heksadesimal jarang digunakan kecuali jika Anda berurusan dengan topik tingkat yang relatif rendah di mana tata letak memori data penting. Menggunakannya mengisyaratkan fakta bahwa itulah situasi yang kita hadapi sekarang.

Juga, saya tidak yakin tentang C #, tetapi saya tahu bahwa di C x << yadalah konstanta waktu kompilasi yang valid. Menggunakan pergeseran bit tampaknya yang paling jelas:

[Flags]
public enum MyEnum
{
    None  = 0,
    Flag1 = 1 << 0,
    Flag2 = 1 << 1,
    Flag3 = 1 << 2,
    Flag4 = 1 << 3,
    Flag5 = 1 << 4
}
ada-untuk semua
sumber
7
Itu sangat menarik dan sebenarnya C # enum yang valid juga.
Adi Lester
13
+1 dengan notasi ini Anda tidak pernah melakukan kesalahan dalam perhitungan nilai enum
Sergey Berezovskiy
1
@ AllonGuralnek: Apakah kompilator menetapkan posisi bit unik dengan anotasi [Flags]? Umumnya dimulai dari 0 dan bertambah 1, jadi nilai enum apa pun yang ditetapkan 3 (desimal) akan menjadi 11 dalam biner, menyetel dua bit.
Eric J.
2
@ Eric: Huh, saya tidak tahu mengapa tetapi saya selalu yakin bahwa itu memberikan nilai-nilai kekuatan dua. Saya baru saja memeriksa, dan saya kira saya salah.
Allon Guralnek
38
Fakta menyenangkan lainnya dengan x << ynotasi. 1 << 10 = KB, 1 << 20 = MB, 1 << 30 = GBDan sebagainya. Sangat menyenangkan jika Anda ingin membuat array 16 KB untuk buffer Anda bisa pergivar buffer = new byte[16 << 10];
Scott Chamberlain
43

Ini membuatnya mudah untuk melihat bahwa ini adalah bendera biner .

None  = 0x0,  // == 00000
Flag1 = 0x1,  // == 00001
Flag2 = 0x2,  // == 00010
Flag3 = 0x4,  // == 00100
Flag4 = 0x8,  // == 01000
Flag5 = 0x10  // == 10000

Meskipun perkembangannya membuatnya lebih jelas:

Flag6 = 0x20  // == 00100000
Flag7 = 0x40  // == 01000000
Flag8 = 0x80  // == 10000000
Oded
sumber
3
Saya sebenarnya menambahkan 0 di depan 0x1, 0x2, 0x4, 0x8 ... Jadi saya mendapatkan 0x01, 0x02, 0x04, 0x08 dan 0x10 ... Saya merasa lebih mudah dibaca. Apakah saya mengacaukan sesuatu?
LightStriker
4
@Light - Tidak sama sekali. Ini sangat umum sehingga Anda dapat melihat bagaimana ini sejajar. Hanya membuat bit lebih eksplisit :)
Oded
2
@LightStriker hanya akan membuangnya di luar sana yang tidak peduli jika Anda tidak menggunakan hex. Nilai yang dimulai dengan hanya nol diartikan sebagai oktal. Jadi 012sebenarnya 10.
Jonathon Reinhart
@JonathonRainhart: Itu saya tahu. Tapi saya selalu menggunakan hex saat menggunakan bitfield. Saya tidak yakin saya akan merasa aman menggunakannya int. Aku tahu itu bodoh ... tapi kebiasaan sulit hilang.
LightStriker
36

Saya pikir itu hanya karena urutannya selalu 1,2,4,8 dan kemudian tambahkan 0.
Seperti yang Anda lihat:

0x1 = 1 
0x2 = 2
0x4 = 4
0x8 = 8
0x10 = 16
0x20 = 32
0x40 = 64
0x80 = 128
0x100 = 256
0x200 = 512
0x400 = 1024
0x800 = 2048

dan seterusnya, selama Anda mengingat urutan 1-2-4-8, Anda dapat membuat semua flag berikutnya tanpa harus mengingat pangkat 2

VRonin
sumber
13

Karena [Flags]artinya enumnya benar-benar bitfield . Dengan [Flags]Anda dapat menggunakan operator bitwise AND ( &) dan OR ( |) untuk menggabungkan flag. Ketika berhadapan dengan nilai biner seperti ini, hampir selalu lebih jelas untuk menggunakan nilai heksadesimal. Inilah alasan utama kami menggunakan heksadesimal di tempat pertama. Setiap bersesuaian karakter hex ke persis satu menggigit (empat bit). Dengan desimal, pemetaan 1-ke-4 ini tidak berlaku.

Jonathon Reinhart
sumber
2
Kemampuan untuk menggunakan operasi bitwise sebenarnya tidak ada hubungannya dengan atribut flags.
Mattias Nordqvist
4

Karena ada cara mekanis dan sederhana untuk menggandakan kekuatan dua dalam hex. Dalam desimal, ini sulit. Ini membutuhkan perkalian yang panjang di kepala Anda. Dalam hex itu adalah perubahan sederhana. Anda dapat melakukan ini sampai 1UL << 63yang tidak dapat Anda lakukan dalam desimal.

usr
sumber
1
Saya pikir ini yang paling masuk akal. Untuk enum dengan sekumpulan besar nilai, ini adalah cara termudah untuk melakukannya (dengan kemungkinan pengecualian pada contoh @ Hypercube).
Adi Lester
3

Karena lebih mudah untuk mengikuti manusia dimana bit-bit tersebut berada di dalam bendera. Setiap digit heksadesimal dapat memuat biner 4 bit.

0x0 = 0000
0x1 = 0001
0x2 = 0010
0x3 = 0011

... and so on

0xF = 1111

Biasanya Anda ingin flag Anda tidak tumpang tindih dengan bit, cara termudah untuk melakukan dan memvisualisasikannya adalah menggunakan nilai heksadesimal untuk mendeklarasikan flag Anda.

Jadi, jika Anda membutuhkan flag dengan 16 bit, Anda akan menggunakan 4 digit nilai heksadesimal dan dengan demikian Anda dapat menghindari nilai yang salah:

0x0001 //= 1 = 000000000000 0001
0x0002 //= 2 = 000000000000 0010
0x0004 //= 4 = 000000000000 0100
0x0008 //= 8 = 000000000000 1000
...
0x0010 //= 16 = 0000 0000 0001 0000
0x0020 //= 32 = 0000 0000 0010 0000
...
0x8000 //= 32768 = 1000 0000 0000 0000
Hanya kamu
sumber
Penjelasan ini bagus .... satu-satunya hal yang hilang adalah persamaan biner untuk menunjukkan garis akhir bit, camilan, dan byte;)
GoldBishop