เหตุใดจึงมักกำหนดแฟล็ก enums ด้วยค่าฐานสิบหก


122

หลายครั้งฉันเห็นการประกาศ flag enum ที่ใช้ค่าฐานสิบหก ตัวอย่างเช่น:

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

เมื่อฉันประกาศ enum ฉันมักจะประกาศเช่นนี้:

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

มีเหตุผลหรือเหตุผลที่บางคนเลือกที่จะเขียนค่าเป็นเลขฐานสิบหกแทนที่จะเป็นทศนิยม? อย่างที่ฉันเห็นมันง่ายกว่าที่จะสับสนเมื่อใช้ค่าฐานสิบหกและตั้งใจเขียนFlag5 = 0x16Flag5 = 0x10แทน


4
อะไรทำให้มีโอกาสน้อยที่คุณจะเขียน10แทนที่จะ0x10ใช้เลขฐานสิบ โดยเฉพาะอย่างยิ่งเนื่องจากสิ่งเหล่านี้เป็นเลขฐานสองที่เรากำลังติดต่ออยู่และฐานสิบหกสามารถแปลงเป็น / จากเลขฐานสองได้เล็กน้อย? 0x111เป็นเรื่องที่น่ารำคาญในการแปลในหัวน้อยกว่า273...
cHao

4
เป็นเรื่องน่าเสียดายที่ C # ไม่มีไวยากรณ์ที่ไม่จำเป็นต้องเขียนยกกำลังสองอย่างชัดเจน
พันเอก Panic

คุณกำลังทำอะไรที่ไร้สาระที่นี่ เจตนาที่อยู่เบื้องหลังแฟล็กคือพวกมันจะรวมกันแบบบิต แต่ชุดค่าผสมระดับบิตไม่ใช่องค์ประกอบของประเภท ค่าFlag1 | Flag2คือ 3 และ 3 MyEnumไม่ตรงกับค่าโดเมนใด
Kaz

คุณเห็นสิ่งนั้นที่ไหน? พร้อมรีเฟลกเตอร์?
giammin

@giammin เป็นคำถามทั่วไปไม่เกี่ยวกับการใช้งานเฉพาะ คุณสามารถใช้โครงการโอเพนซอร์สหรือเพียงแค่โค้ดที่มีอยู่บนเน็ตเช่น
Adi Lester

คำตอบ:


184

เหตุผลอาจแตกต่างกัน แต่ข้อดีที่ฉันเห็นคือเลขฐานสิบหกเตือนคุณ: "เอาล่ะเราไม่ได้เกี่ยวข้องกับตัวเลขในโลกฐานสิบที่มนุษย์ประดิษฐ์ขึ้นเองอีกต่อไปเรากำลังจัดการกับบิต - โลกของเครื่องจักร - และเรา 'จะเล่นตามกฎของมัน " ไม่ค่อยมีการใช้เลขฐานสิบหกเว้นแต่ว่าคุณกำลังจัดการกับหัวข้อที่ค่อนข้างต่ำซึ่งเค้าโครงหน่วยความจำของข้อมูลมีความสำคัญ การใช้มันบ่งบอกถึงความจริงที่ว่านั่นคือสถานการณ์ที่เราอยู่ในตอนนี้

นอกจากนี้ฉันไม่แน่ใจเกี่ยวกับ C # แต่ฉันรู้ว่าใน C x << yเป็นค่าคงที่เวลาคอมไพล์ที่ถูกต้อง การใช้การกะบิตดูเหมือนชัดเจนที่สุด:

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

7
มันน่าสนใจมากและมันก็เป็น C # enum ที่ถูกต้องเช่นกัน
Adi Lester

13
+1 ด้วยสัญกรณ์นี้คุณจะไม่มีข้อผิดพลาดในการคำนวณค่า enum
Sergey Berezovskiy

1
@AllonGuralnek: คอมไพเลอร์กำหนดตำแหน่งบิตเฉพาะให้กับคำอธิบายประกอบ [Flags] หรือไม่? โดยทั่วไปจะเริ่มต้นที่ 0 และเพิ่มขึ้นทีละ 1 ดังนั้นค่า enum ใด ๆ ที่กำหนด 3 (ทศนิยม) จะเป็น 11 ในไบนารีโดยตั้งค่าเป็นสองบิต
Eric J.

2
@ เอริก: หือฉันไม่รู้ว่าทำไม แต่ฉันมั่นใจเสมอว่ามันกำหนดค่าของพลังสองอย่าง ฉันเพิ่งตรวจสอบและฉันเดาว่าฉันผิด
Allon Guralnek

38
ข้อเท็จจริงที่น่าสนใจอีกประการหนึ่งกับx << yสัญกรณ์ 1 << 10 = KB, 1 << 20 = MB, 1 << 30 = GBและอื่น ๆ มันดีมากถ้าคุณต้องการสร้างอาร์เรย์ 16 KB สำหรับบัฟเฟอร์คุณสามารถไปได้var buffer = new byte[16 << 10];
Scott Chamberlain

43

ทำให้ง่ายต่อการดูว่าสิ่งเหล่านี้เป็นแฟล็กไบนารี

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

แม้ว่าความก้าวหน้าจะทำให้ชัดเจนยิ่งขึ้น:

Flag6 = 0x20  // == 00100000
Flag7 = 0x40  // == 01000000
Flag8 = 0x80  // == 10000000

3
ที่จริงฉันเพิ่ม 0 หน้า 0x1, 0x2, 0x4, 0x8 ... ดังนั้นฉันจึงได้ 0x01, 0x02, 0x04, 0x08 และ 0x10 ... ฉันพบว่ามันอ่านง่ายกว่า ฉันทำอะไรผิดพลาดหรือเปล่า?
LightStriker

4
@ แสง - ไม่เลย เป็นเรื่องปกติมากที่คุณจะเห็นว่าการจัดแนวเหล่านี้เป็นอย่างไร เพียงแค่ทำให้บิตที่ชัดเจนมากขึ้น :)
Oded

2
@LightStriker แค่จะโยนมันออกไปที่นั่นไม่ว่าคุณจะไม่ได้ใช้ hex ค่าที่ขึ้นต้นด้วยศูนย์เท่านั้นจะตีความเป็นฐานแปด ดังนั้นเป็นจริง012 10
Jonathon Reinhart

@JonathonRainhart: ที่ฉันรู้ แต่ฉันมักจะใช้ hex เสมอเมื่อใช้ bitfields ฉันไม่แน่ใจว่าฉันจะรู้สึกปลอดภัยเมื่อใช้intแทน ฉันรู้ว่ามันโง่ ... แต่นิสัยตายยาก
LightStriker

36

ฉันคิดว่ามันเป็นเพียงเพราะลำดับเป็น 1,2,4,8 เสมอแล้วบวก 0
อย่างที่คุณเห็น:

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

และต่อไปตราบใดที่คุณจำลำดับ 1-2-4-8 ได้คุณสามารถสร้างแฟล็กที่ตามมาทั้งหมดได้โดยไม่ต้องจำพลังของ 2


13

เพราะ[Flags]หมายความว่า enum เป็นบิตฟิลด์จริงๆ bitfieldด้วย[Flags]คุณสามารถใช้ค่าที่เหมาะสมและ ( &) และหรือ ( |) ผู้ประกอบการที่จะรวมธง เมื่อจัดการกับค่าไบนารีเช่นนี้การใช้ค่าฐานสิบหกมักจะชัดเจนกว่าเสมอ นี่คือเหตุผลที่เราใช้เลขฐานสิบหกตั้งแต่แรก แต่ละตัวละครสอดคล้องฐานสิบหกไปว่าหนึ่งตอด (สี่บิต) ด้วยทศนิยมการแมป 1 ถึง 4 นี้ไม่ถือเป็นจริง


2
ความสามารถในการใช้การดำเนินการแบบบิตไม่เกี่ยวข้องกับแอตทริบิวต์แฟล็ก
Mattias Nordqvist

4

เนื่องจากมีวิธีเชิงกลที่เรียบง่ายในการเพิ่มกำลังสองของสองในฐานสิบหก ในฐานสิบนี่ยาก ต้องมีการคูณยาวในหัวของคุณ ในฐานสิบหกเป็นการเปลี่ยนแปลงง่ายๆ คุณสามารถดำเนินการนี้ไปจนถึงจุด1UL << 63ที่คุณไม่สามารถทำทศนิยมได้


1
ฉันคิดว่านี่เข้าท่าที่สุด สำหรับ enums ที่มีชุดค่าจำนวนมากจะเป็นวิธีที่ง่ายที่สุด (ยกเว้นตัวอย่างของ @ Hypercube)
Adi Lester

3

เนื่องจากมันง่ายกว่าที่จะติดตามสำหรับมนุษย์ที่บิตอยู่ในแฟล็ก เลขฐานสิบหกแต่ละตัวสามารถใส่เลขฐานสอง 4 บิตได้

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

... and so on

0xF = 1111

โดยทั่วไปคุณต้องการให้แฟล็กของคุณไม่ทับซ้อนบิตวิธีที่ง่ายที่สุดในการทำและแสดงภาพคือการใช้ค่าฐานสิบหกเพื่อประกาศแฟล็กของคุณ

ดังนั้นหากคุณต้องการแฟล็กที่มี 16 บิตคุณจะใช้ค่าฐานสิบหก 4 หลักและด้วยวิธีนี้คุณสามารถหลีกเลี่ยงค่าที่ผิดพลาดได้:

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

คำอธิบายนี้ดีมาก .... สิ่งเดียวที่ขาดหายไปคือการเทียบเท่าไบนารีเพื่อแสดงบรรทัดสูงสุดของบิตแทะและไบต์;)
GoldBishop
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.