ทำไมการอนุญาต enum มักจะมีค่า 0, 1, 2, 4


159

ทำไมคนมักใช้ค่า enum ชอบ0, 1, 2, 4, 8และไม่0, 1, 2, 3, 4?

สิ่งนี้เกี่ยวข้องกับการทำงานของบิตหรือไม่?

ฉันขอขอบคุณตัวอย่างข้อมูลขนาดเล็กเกี่ยวกับวิธีการใช้งานอย่างถูกต้อง :)

[Flags]
public enum Permissions
{
    None   = 0,
    Read   = 1,
    Write  = 2,
    Delete = 4
}


25
ฉันไม่เห็นด้วยกับการลงคะแนนดูปองต์
zzzzBov

วิธี UNIX ในการตั้งค่าการอนุญาตขึ้นอยู่กับตรรกะเดียวกัน
Rudy

3
@Pascal: คุณอาจพบว่ามีประโยชน์ในการอ่านเกี่ยวกับBitwise หรือ (และBitwise AND ) ซึ่งเป็นสิ่งที่|(และ&) เป็นตัวแทน คำตอบที่หลากหลายถือว่าคุณคุ้นเคยกับมัน
Brian

2
@IAdapter ฉันเห็นได้ว่าทำไมคุณถึงคิดว่าเพราะคำตอบของทั้งคู่เหมือนกัน แต่ฉันคิดว่าคำถามนั้นแตกต่างกัน คำถามอื่น ๆ เพียงขอตัวอย่างหรือคำอธิบายของคุณลักษณะค่าสถานะใน C # คำถามนี้ดูเหมือนจะเกี่ยวกับแนวคิดของการตั้งค่าสถานะบิตและพื้นฐานที่อยู่เบื้องหลังพวกเขา
Jeremy S

คำตอบ:


268

เพราะมันเป็นพลังของสองและฉันสามารถทำสิ่งนี้:

var permissions = Permissions.Read | Permissions.Write;

และบางทีในภายหลัง ...

if( (permissions & Permissions.Write) == Permissions.Write )
{
    // we have write access
}

มันเป็นฟิลด์บิตที่แต่ละบิตตั้งค่าสอดคล้องกับสิทธิ์บางอย่าง (หรือสิ่งที่ค่าที่แจกแจงสอดคล้องกับเหตุผล) หากสิ่งเหล่านี้ถูกกำหนดเพราะ1, 2, 3, ...คุณจะไม่สามารถใช้ตัวดำเนินการระดับบิตในแบบนี้และได้ผลลัพธ์ที่มีความหมาย เพื่อเจาะลึก ...

Permissions.Read   == 1 == 00000001
Permissions.Write  == 2 == 00000010
Permissions.Delete == 4 == 00000100

สังเกตรูปแบบที่นี่? ตอนนี้ถ้าเราใช้ตัวอย่างดั้งเดิมของฉันคือ

var permissions = Permissions.Read | Permissions.Write;

จากนั้น ...

permissions == 00000011

ดู? มีการตั้งค่าทั้งสองReadและWriteบิตและฉันสามารถตรวจสอบได้อย่างอิสระ (โปรดสังเกตว่าDeleteบิตไม่ได้ถูกตั้งค่าและดังนั้นค่านี้จึงไม่อนุญาตให้ลบ

อนุญาตให้หนึ่งเก็บหลายแฟล็กในฟิลด์เดียวของบิต


2
@ Malcolm: มัน; myEnum.IsSet. ฉันเห็นว่านี่เป็นสิ่งที่ไร้ประโยชน์อย่างสมบูรณ์และให้บริการเพียงเพื่อลดการพิมพ์ แต่ meh
Ed S.

1
คำตอบที่ดี แต่คุณควรพูดถึงว่าทำไมจึงใช้แอททริบิวของ Flags และเมื่อคุณไม่ต้องการใช้ค่าสถานะกับ enums เช่นกัน
Andy

3
@Andy: ที่จริงแล้วFlagsคุณสมบัติไม่น้อยไปกว่าให้คุณพิมพ์ 'สวย' iirc คุณสามารถใช้ค่าที่แจกแจงเป็นค่าสถานะโดยไม่คำนึงถึงการมีอยู่ของแอตทริบิวต์
Ed S.

3
@detly: เพราะถ้าคำสั่งใน C # ต้องการนิพจน์บูลีน 0ไม่ใช่false; เป็นfalse แต่คุณสามารถเขียนfalse if((permissions & Permissions.Write) > 0)
Ed S.

2
แทนที่จะเป็น 'เล่ห์เหลี่ยม' (permissions & Permissions.Write) == Permissions.Writeคุณสามารถใช้งานได้แล้วenum.HasFlag()
Louis Kottmann

147

หากยังไม่ชัดเจนจากคำตอบอื่น ๆ ลองคิดดังนี้:

[Flags] 
public enum Permissions 
{   
   None = 0,   
   Read = 1,     
   Write = 2,   
   Delete = 4 
} 

เป็นวิธีที่สั้นกว่าในการเขียน:

public enum Permissions 
{   
    DeleteNoWriteNoReadNo = 0,   // None
    DeleteNoWriteNoReadYes = 1,  // Read
    DeleteNoWriteYesReadNo = 2,  // Write
    DeleteNoWriteYesReadYes = 3, // Read + Write
    DeleteYesWriteNoReadNo = 4,   // Delete
    DeleteYesWriteNoReadYes = 5,  // Read + Delete
    DeleteYesWriteYesReadNo = 6,  // Write + Delete
    DeleteYesWriteYesReadYes = 7, // Read + Write + Delete
} 

มีความเป็นไปได้แปดอย่าง แต่คุณสามารถแทนพวกเขาเป็นการรวมกันของสมาชิกสี่คน หากมีความเป็นไปได้สิบหกข้อคุณสามารถแสดงให้พวกเขารวมกันเป็นสมาชิกเพียงห้าคน หากมีความเป็นไปได้สี่พันล้านครั้งคุณสามารถแสดงให้พวกเขารวมกันเป็นสมาชิกเพียง 33 คน! เห็นได้ชัดว่าดีกว่าที่จะมีสมาชิกเพียง 33 คนแต่ละคน (ยกเว้นศูนย์) ที่มีพลังสองคนมากกว่าที่จะพยายามตั้งชื่อรายการสี่พันล้านรายการใน Enum


32
+1 สำหรับภาพลักษณ์ของenumสมาชิกสี่พันล้านคน และส่วนที่น่าเศร้าก็คือบางคนอาจลองใช้ดู
Daniel Pryden

23
@DanielPryden ในฐานะผู้อ่านรายวันของ WTF รายวันฉันเชื่อว่า
ปุย

1
2 ^ 33 = ~ 8.6 พันล้าน สำหรับค่าที่แตกต่างกัน 4 พันล้านค่าเพียง 32 บิตเท่านั้น
CVn

5
@ MichaelKjörlingหนึ่งใน 33 เป็นค่าเริ่มต้น 0
ratchet freak

@ MichaelKjörling: เพื่อความยุติธรรมมีเพียง 32 สมาชิกที่เป็นพลังของ 2 เนื่องจาก 0 ไม่ใช่พลังของสอง ดังนั้น "สมาชิก 33 คนพลังของสองคน" จึงไม่ถูกต้องแม่นยำ (เว้นแต่คุณจะนับ2 ** -infinityเป็นพลังของทั้งสอง)
Brian

36

เนื่องจากค่าเหล่านี้แทนตำแหน่งบิตที่ไม่ซ้ำกันในไบนารี:

1 == binary 00000001
2 == binary 00000010
4 == binary 00000100

เป็นต้น

1 | 2 == binary 00000011

แก้ไข:

3 == binary 00000011

3 ในไบนารีจะถูกแทนด้วยค่า 1 ในทั้งสถานที่และสถานที่สอง 1 | 2มันเป็นจริงเช่นเดียวกับค่า ดังนั้นเมื่อคุณพยายามใช้ตำแหน่งไบนารีเป็นแฟล็กเพื่อเป็นตัวแทนของบางสถานะ 3 มักไม่ได้มีความหมาย (เว้นแต่จะมีค่าตรรกะที่จริงเป็นการรวมกันของทั้งสอง)

สำหรับการชี้แจงเพิ่มเติมคุณอาจต้องการขยายตัวอย่าง enum ดังนี้:

[Flags]
public Enum Permissions
{
  None = 0,   // Binary 0000000
  Read = 1,   // Binary 0000001
  Write = 2,  // Binary 0000010
  Delete = 4, // Binary 0000100
  All = 7,    // Binary 0000111
}

ดังนั้นฉันมีPermissions.AllฉันยังปริยายมีPermissions.Read, Permissions.WriteและPermissions.Delete


และปัญหาเกี่ยวกับ 2 | 3 คืออะไร?
Pascal

1
@Pascal: เนื่องจาก3เป็น11เลขฐานสองนั่นคือมันไม่ได้แมปไปยังบิตชุดเดียวดังนั้นคุณสูญเสียความสามารถในการแมป 1 บิตในตำแหน่งโดยพลการกับค่าที่มีความหมาย
Ed S.

8
@Pascal ใช้วิธีอื่น, 2|3 == 1|3 == 1|2 == 3. ดังนั้นถ้าคุณมีค่ากับไบนารี00000011และธงของคุณรวมค่า1, 2และ3แล้วคุณจะไม่ทราบว่าถ้าค่าที่แสดงถึง1 and 3, 2 and 3, หรือ1 and 2 only 3ทำให้มีประโยชน์น้อยกว่ามาก
yshavit

10
[Flags]
public Enum Permissions
{
    None   =    0; //0000000
    Read   =    1; //0000001
    Write  = 1<<1; //0000010
    Delete = 1<<2; //0000100
    Blah1  = 1<<3; //0001000
    Blah2  = 1<<4; //0010000
}

ฉันคิดว่าการเขียนแบบนี้ง่ายต่อการเข้าใจและอ่านและคุณไม่จำเป็นต้องคำนวณ


5

สิ่งเหล่านี้ใช้เพื่อแทนค่าสถานะบิตซึ่งอนุญาตให้รวมค่า enum ฉันคิดว่ามันชัดเจนกว่าถ้าคุณเขียนค่าในรูปแบบเลขฐานสิบหก

[Flags]
public Enum Permissions
{
  None =  0x00,
  Read =  0x01,
  Write = 0x02,
  Delete= 0x04,
  Blah1 = 0x08,
  Blah2 = 0x10
}

4
@Pascal: บางทีมันอาจจะอ่านได้มากขึ้นสำหรับคุณในเวลานี้ แต่เมื่อคุณได้รับประสบการณ์การดูไบต์ในฐานสิบหกกลายเป็นลักษณะที่สอง ตัวเลขสองหลักในฐานสิบหกแผนที่ไปยังหนึ่งไบต์แผนที่ถึง 8 บิต (ดี ... ไบต์มักจะ 8 บิตต่อไป ... ไม่จริงเสมอไป แต่สำหรับตัวอย่างนี้มันก็โอเคที่จะพูดคุยทั่วไป)
Ed S.

5
@Pascal รวดเร็วคุณจะได้อะไรเมื่อคุณคูณ4194304ด้วย 2 แล้วไง0x400000ล่ะ ง่ายต่อการจดจำ0x800000ว่าเป็นคำตอบที่ถูกต้องมากกว่า8388608และยังมีข้อผิดพลาดน้อยกว่าในการพิมพ์ค่าเลขฐานสิบหก
phoog

6
เป็นการง่ายกว่ามากที่จะบอกได้ว่าหากตั้งค่าสถานะของคุณอย่างถูกต้องแล้ว (เช่นกำลังของ 2) หากคุณใช้เลขฐานสิบหก เป็น0x10000อำนาจของสอง? ใช่มันเริ่มต้นด้วย 1, 2, 4 หรือ 8 และมี 0 ทั้งหมดหลังจากนั้น คุณไม่จำเป็นต้องแปลทางใจ 0x10 ถึง 16 (แม้ว่าการทำเช่นนั้นจะกลายเป็นลักษณะที่สองในที่สุด) เพียงแค่คิดว่ามันเป็น "พลังบางส่วนของ 2"
Brian

1
ฉันกับ Jared อย่างแน่นอนเกี่ยวกับการจดบันทึกเลขฐานสิบหก คุณเพียงแค่ใช้ 1 2 4 8 และเปลี่ยน
bevacqua

1
โดยส่วนตัวแล้วฉันชอบใช้เช่น P_READ = 1 << 0, P_WRITE = 1 <, 1, P_RW = P_READ | P_WRITE ฉันไม่แน่ใจว่าการพับแบบคงที่นั้นทำงานใน C # หรือไม่ แต่มันใช้ได้ดีใน C / C ++ (เช่นเดียวกับ Java ฉันคิดว่า)
ปุย

1

นี่เป็นความคิดเห็นที่มากขึ้น แต่เนื่องจากไม่รองรับการจัดรูปแบบฉันแค่ต้องการรวมวิธีการที่ฉันใช้ในการตั้งค่าการแจงนับธง:

[Flags]
public enum FlagTest
{
    None = 0,
    Read = 1,
    Write = Read * 2,
    Delete = Write * 2,
    ReadWrite = Read|Write
}

ฉันพบว่าวิธีการนี้มีประโยชน์เป็นพิเศษในระหว่างการพัฒนาในกรณีที่คุณต้องการรักษาธงของคุณตามลำดับตัวอักษร หากคุณพิจารณาว่าคุณต้องการเพิ่มค่าแฟล็กใหม่คุณสามารถแทรกค่าตามตัวอักษรและค่าเดียวที่คุณต้องเปลี่ยนคือค่าที่นำหน้า

อย่างไรก็ตามโปรดทราบว่าเมื่อโซลูชันได้รับการเผยแพร่ไปยังระบบการผลิตใด ๆ (โดยเฉพาะอย่างยิ่งถ้า enum ถูกเปิดเผยโดยไม่มีการเชื่อมต่อที่แน่นหนาเช่นผ่านเว็บเซอร์วิส) จึงแนะนำให้เลือกไม่ให้เปลี่ยนค่าที่มีอยู่ภายใน enum


1

มีคำตอบที่ดีสำหรับสิ่งนี้…ฉันจะบอก .. ถ้าคุณไม่ชอบหรือไม่สามารถเข้าใจได้อย่างง่ายดายว่า<<ไวยากรณ์กำลังพยายามที่จะแสดง .. ฉันเองชอบทางเลือกอื่น (และกล้าพูดแบบตรงไปตรงมาประกาศ enum) ...

typedef NS_OPTIONS(NSUInteger, Align) {
    AlignLeft         = 00000001,
    AlignRight        = 00000010,
    AlignTop          = 00000100,
    AlignBottom       = 00001000,
    AlignTopLeft      = 00000101,
    AlignTopRight     = 00000110,
    AlignBottomLeft   = 00001001,
    AlignBottomRight  = 00001010
};

NSLog(@"%ld == %ld", AlignLeft | AlignBottom, AlignBottomLeft);

LOG 513 == 513

ง่ายกว่ามาก (สำหรับตัวเองอย่างน้อย) ที่จะเข้าใจ จัดเรียงสิ่งที่ต้องการ ... อธิบายผลลัพธ์ที่คุณต้องการรับผลลัพธ์ที่คุณต้องการ .. ไม่จำเป็นต้องมี "การคำนวณ"

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