如何删除OR'd枚举的项目?


180

我有一个像这样的枚举:

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

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

如何从可变颜色中删除蓝色?


2
小提示:C#中的按位枚举应在其上方获得[Flags]属性。
Nyerguds

@Nyerguds,您能解释一下为什么要获取该属性吗?
乙烷

它在调试时提供了更好的IntelliSense支持,因为它会将未知的枚举值识别为现有值的组合。
Nyerguds

1
它还提供了更有意义的.ToString()价值。例如RED, BLUE, YELLOW而不是22
Sinjai

Answers:


328

您需要&使用~“ BLUE” 的(补码)。

补码运算符实质上是反转或“翻转”给定数据类型的所有位。因此,如果您使用AND运算子(&)与某个值一起使用(让我们将该值称为“ X”),并将其与一个或多个设置位的补码(让我们调用这些位Q及其补码~Q),那么该语句将X & ~Q清除在QX并返回结果。

因此,要删除或清除这些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

好答案。我认为您的意思是代码示例中的“&=”不是“ =&”;)
Dave Jellison

47

其他答案是正确的,但是要从上面特别去除蓝色,您可以这样写:

colors &= ~Blah.BLUE;

9

And not 它...............................

Blah.RED | Blah.YELLOW == 
   (Blah.RED | Blah.BLUE | Blah.YELLOW) & ~Blah.BLUE;

7

认为这对像我这样跌倒在这里的其他人可能有用。

请注意如何处理可能设置为== 0的任何枚举值(有时枚举处于Unknown或Idle状态可能会有所帮助)。依靠这些位操作操作时会引起问题。

同样,当枚举值是2值的其他幂的组合时,例如

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作为标志。我通常会设置标志枚举喜欢这样。我看不出从状态状态中指定0值会带来什么好处。毕竟,总的来说,您可以关心程序员友好的名称(Red, Blue, Green)而不是基础值,是吗?
Sinjai

在很多情况下,具有noneunknownunset= = 0的条目是一件好事,因为您不能总是假设特定的值是默认设置,例如,如果您只有Red,Blue和Green,您将拥有Red作为默认值(例如,枚举在创建时是具有的值还是用户未对其进行修改?)
Sverrir Sigmundarson

您难道不能让Unset = 1 1, 2, 4, 8用作值吗?如果您需要默认值,那就是默认构造函数的意思,对吧?为了易于阅读,我喜欢尽可能使内容保持明确,并且尽管未提供任何信息,但依赖枚举将默认值设置为default(int)0),尽管它可能是任何开发人员都知道的信息,但无论如何我还是不太喜欢。编辑:等等,将Unset设为0会阻止某些事情发生Unset | Blue吗?
Sinjai

您在编辑Sinjai中的正确轨道上,保持未设置的值== 0实际上可以解决一些问题。一个是您提到的内容,另一个是如果您未设置所有枚举值,则如果将其设置为零,则不需要“ Unset”的特殊情况。(例如,如果您Red & ~Red最终以零结尾,则必须有代码来检查这种情况并显式分配Unset值)
Sverrir Sigmundarson

知道了 您是否经常发现自己正在处理未设置的标志?
Sinjai

2

那xor(^)呢?

鉴于您要删除的标志已存在,因此它将起作用..如果没有,则必须使用&。

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

为了简化标志枚举并通过避免使用倍数使其更好地读取,我们可以使用位移。(摘自一篇出色的文章,《关于枚举旗帜的大辩论》

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

并清除所有位

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.