จะลบรายการสำหรับ OR enum ได้อย่างไร


181

ฉันมี Enum เช่น:

public enum Blah
{
    RED = 2,
    BLUE = 4,
    GREEN = 8,
    YELLOW = 16
}

Blah colors = Blah.RED | Blah.BLUE | Blah.YELLOW;

ฉันจะลบสีน้ำเงินออกจากสีตัวแปรได้อย่างไร


2
ข้อความเล็กน้อย: enum bitwise ใน C # ควรได้รับแอตทริบิวต์ [ค่าสถานะ] ด้านบน
Nyerguds

@ Nyerguds คุณช่วยอธิบายได้ไหมว่าทำไมจึงควรได้รับคุณลักษณะนี้
อีเทน

มันให้การสนับสนุน IntelliSense ที่ดีขึ้นในขณะที่ทำการดีบั๊กเพราะมันรับรู้ค่า enum ที่ไม่รู้จักเป็นการรวมกันของค่าที่มีอยู่
Nyerguds

1
นอกจากนี้ยังมีความหมายมากขึ้น.ToString()ค่า เช่นมากกว่าRED, BLUE, YELLOW 22
สินใจ

คำตอบ:


331

คุณจำเป็นต้องใช้&ด้วย~(ส่วนประกอบ) ของ 'BLUE'

โอเปอเรเตอร์ประกอบกลับด้านหรือ 'พลิก' บิตทั้งหมดสำหรับชนิดข้อมูลที่กำหนด เช่นถ้าคุณใช้ANDประกอบการ ( &) มีค่าบางอย่าง (ขอเรียกว่าค่า 'X') และส่วนประกอบของหนึ่งหรือชุดเพิ่มเติมบิต (ขอเรียกบิตเหล่านั้นQและเติมเต็มของพวกเขา~Q) คำสั่งX & ~Qล้างบิตใด ๆ ที่ตั้งอยู่ในQจากXและส่งคืนผลลัพธ์

ดังนั้นเมื่อต้องการลบหรือล้างBLUEบิตคุณต้องใช้คำสั่งต่อไปนี้:

colorsWithoutBlue = colors & ~Blah.BLUE
colors &= ~Blah.BLUE // This one removes the bit from 'colors' itself

คุณยังสามารถระบุหลายบิตเพื่อล้างดังนี้:

colorsWithoutBlueOrRed = colors & ~(Blah.BLUE | Blah.RED)
colors &= ~(Blah.BLUE | Blah.RED) // This one removes both bits from 'colors' itself

หรือสลับกัน ...

colorsWithoutBlueOrRed = colors & ~Blah.BLUE & ~Blah.RED
colors &= ~Blah.BLUE & ~Blah.RED // This one removes both bits from 'colors' itself

ดังนั้นเพื่อสรุป:

  • X | Q ชุดบิต Q
  • X & ~Q ล้างบิต Q
  • ~X พลิก / กลับค่าบิตทั้งหมดใน X

1
ดังนั้นถ้าฉันต้องการลบเพิ่มเติมฉันจะทำ: & = ~ Blah.BLUE & ~ Blah.Green?
Blankman

11
ดีกว่าคือcolors &= ~( Blah.BLUE | Blah.GREEN );
พาร์

2
ผิดปกติฉันแค่คิดว่าจะทำอีกครั้งประมาณ 5 วันที่แล้ว :) คำถามนี้ได้รับการอัพเดทในกล่องจดหมายและข้อความเสียง!
Blankman

คำตอบที่ดี ฉันคิดว่าคุณหมายถึง "และ =" ไม่ "= &" ในตัวอย่างรหัสของคุณ แต่;)
เดฟ Jellison

48

คำตอบอื่น ๆ นั้นถูกต้อง แต่หากต้องการลบสีน้ำเงินโดยเฉพาะคุณต้องเขียน:

colors &= ~Blah.BLUE;


7

คิดว่าสิ่งนี้อาจเป็นประโยชน์สำหรับคนอื่นที่สะดุดเหมือนฉัน

ระวังวิธีที่คุณจัดการค่า enum ใด ๆ ที่คุณอาจกำหนดให้มีค่า == 0 (บางครั้งอาจเป็นประโยชน์ในการมีสถานะ Unknown หรือ Idle สำหรับ enum) มันทำให้เกิดปัญหาเมื่อพึ่งพาการดำเนินการจัดการบิตเหล่านี้

นอกจากนี้เมื่อคุณมีค่า enum ที่เป็นการรวมกันของกำลังสองอื่น ๆ เช่น

public enum Colour
{
    None = 0,  // default value
    RED = 2,
    BLUE = 4,
    GREEN = 8,
    YELLOW = 16,
    Orange = 18  // Combined value of RED and YELLOW
}

ในกรณีเหล่านี้วิธีการขยายเช่นนี้อาจมีประโยชน์:

public static Colour UnSet(this Colour states, Colour state)
{
    if ((int)states == 0)
        return states;

    if (states == state)
        return Colour.None;

    return states & ~state;
}

และยังมีวิธี IsSet ที่เทียบเท่ากันซึ่งจัดการค่าที่รวมกัน (แม้ว่าจะเป็นวิธีที่แฮ็ค)

public static bool IsSet(this Colour states, Colour state)
{
    // By default if not OR'd
    if (states == state)
        return true;

    // Combined: One or more bits need to be set
    if( state == Colour.Orange )
        return 0 != (int)(states & state);

    // Non-combined: all bits need to be set
    return (states & state) == state;
}

สำหรับฉันดูเหมือนว่าวิธีที่ง่ายกว่าคือการหลีกเลี่ยงการใช้ 0 เป็นแฟล็ก ฉันมักจะตั้งค่า enums ธงเช่นนี้ ฉันไม่เห็นวิธีที่คุณได้รับประโยชน์จากการระบุสถานะด้วยค่า 0 ท้ายที่สุดแล้วประเด็นทั้งหมดคือคุณสามารถกังวลกับชื่อที่เป็นมิตรกับโปรแกรมเมอร์ ( Red, Blue, Green) แทนที่จะเป็นค่าพื้นฐานใช่ไหม?
สินใจ

มีnoneหรือunknownหรือunsetรายการที่มีค่า == 0 ในหลายกรณีเป็นสิ่งที่ดีที่คุณไม่เคยคิดว่าคุ้มค่าโดยเฉพาะอย่างยิ่งเป็นชุดเริ่มต้นเช่นถ้าคุณเพียง แต่มีสีแดง, สีฟ้าและสีเขียวที่คุณจะต้องเป็นสีแดง ค่าเริ่มต้น (เช่นค่าที่ enum มีเมื่อมันถูกสร้างขึ้นหรือผู้ใช้ไม่ได้แก้ไขหรือไม่?)
Sverrir Sigmundarson

คุณไม่เพียงแค่มี Unset = 1 และใช้1, 2, 4, 8เป็นค่าของคุณหรือไม่ หากคุณต้องการค่าเริ่มต้นนั่นคือตัวสร้างเริ่มต้นที่ใช่ ฉันชอบที่จะทำให้สิ่งต่าง ๆ มีความชัดเจนที่สุดเท่าที่จะเป็นไปได้เพื่อประโยชน์ในการอ่านและใช้ enum เป็นค่าเริ่มต้นเป็นdefault(int)( 0) เมื่อไม่ได้รับค่าใด ๆ แม้ว่าจะเป็นข้อมูลที่นักพัฒนาคนใดรู้จักก็ตาม แก้ไข: โอ้เดี๋ยวก่อนจะทำให้ Unset = 0 ป้องกันบางสิ่งบางอย่างจากการถูกUnset | Blue?
สินใจ

คุณกำลังติดตามที่ถูกต้องในการแก้ไข Sinjai ของคุณรักษาค่า unset == 0 แก้ปัญหาบางอย่างได้จริง สิ่งหนึ่งคือสิ่งที่คุณพูดถึงและอีกอย่างหนึ่งคือหากคุณไม่ได้กำหนดค่า enum ทั้งหมดคุณไม่จำเป็นต้องมีกรณีพิเศษสำหรับ "Unset" หากตั้งค่าเป็นศูนย์ (เช่นถ้าคุณRed & ~Redจบด้วยศูนย์คุณจะต้องมีรหัสเพื่อตรวจสอบกรณีนี้และกำหนดUnsetค่าอย่างชัดเจน)
Sverrir Sigmundarson

Gotcha คุณมักจะพบว่าตัวเองกำลังจัดการกับธงไม่ได้ตั้งค่า?
Sinjai

2

แล้ว xor (^) ล่ะ?

เนื่องจาก FLAG ที่คุณพยายามลบอยู่นั้นจะมีการทำงาน .. ถ้าไม่คุณจะต้องใช้ &

public enum Colour
{
    None = 0,  // default value
    RED = 2,
    BLUE = 4,
    GREEN = 8,
    YELLOW = 16,
    Orange = 18  // Combined value of RED and YELLOW
}

colors = (colors ^ Colour.RED) & colors;

1

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

[FLAG]
Enum Blah
{
   RED = 1,
   BLUE = 1 << 1,
   GREEN = 1 << 2,
   YELLOW = 1 << 3
}

และเพื่อล้างบิตทั้งหมด

private static void ClearAllBits()
{
    colors = colors & ~colors;
}

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