Как удалить элемент для перечисления OR'd?


182

У меня есть перечисление как:

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.
Синджай,

Ответы:


333

Вам нужно &это с ~(дополнением) «СИНИЙ».

Оператор дополнения по существу инвертирует или «переворачивает» все биты для данного типа данных. Таким образом, если вы используете ANDoperator ( &) с некоторым значением (давайте назовем это значение '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 дней назад :) Этот вопрос обновлен в моем почтовом ящике и вуаля!
Бланкмен

Отличный ответ. Я думаю, что вы имеете в виду "& =" not "= &" в своих примерах кода;)
Дейв Джеллисон

49

Другие ответы верны, но для того, чтобы специально удалить синий цвет из вышеперечисленного, вы должны написать:

colors &= ~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), а не о базовых значениях, да?
Синджай,

Наличие noneили unknownили unsetзаписи со значением == 0 во многих случаях хорошо, так как вы не всегда можете предположить, что определенное значение является набором по умолчанию, например, если у вас есть только красный, синий и зеленый, вы будете иметь красный как значение по умолчанию (например, значение, которое enum имеет при его создании или пользователь не изменил его?)
Sverrir Sigmundarson

Разве вы не можете просто установить Unset = 1 и использовать в 1, 2, 4, 8качестве своих значений? Если вам нужно значение по умолчанию, для этого нужны конструкторы по умолчанию, верно? Мне нравится держать вещи настолько явными, насколько это возможно, для удобства чтения, и полагаться на перечисление по умолчанию на default(int)( 0), когда ему не присвоено значение, несмотря на то, что информация, по-видимому, известна любому разработчику, в любом случае слишком подло. Редактировать: Ой, подождите, будет ли Unset = 0 препятствовать чему-либо быть Unset | Blue?
Синджай,

Вы находитесь на правильном пути в вашем редакторе Синджай, сохраняя неустановленное значение == 0 фактически решает несколько проблем. Один из них - то, что вы упомянули, а другой - если вы сбросили все значения перечисления, тогда вам не нужен особый случай для «Unset», если он установлен на ноль. (например, если вы в Red & ~Redитоге получите ноль, вам потребуется код для проверки этого случая и явное присвоение Unsetзначения)
Sverrir Sigmundarson

Попался. Вы часто сталкиваетесь с неустановленными флагами?
Синджай,

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» )

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

а также очистить все биты

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.