Як видалити елемент для перерахунку АБО?


181

У мене є перелік, як:

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

Відповіді:


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?
Бланкер

11
Кращеcolors &= ~( Blah.BLUE | Blah.GREEN );
пар

2
як незвично, я просто думав, як це зробити ще раз близько 5 днів тому :) Це питання оновлено у моїй вхідній та вуалі!
Бланкер

Чудова відповідь. Я думаю, ти маєш на увазі "& =" не "= &" у своїх зразках коду;)
Дейв Джеллісон

48

Інші відповіді правильні, але щоб конкретно видалити синій із зазначеного вище, ви напишете:

colors &= ~Blah.BLUE;


7

Думав, що це може бути корисно для інших людей, які натрапили сюди, як я.

Будьте уважні, як ви обробляєте будь-які значення перерахунків, які можуть бути встановлені як значення == 0 (іноді може бути корисним, щоб мати значення невідомого або непрацюючого). Це створює проблеми при покладанні на ці операції маніпуляції бітами.

Також коли у вас є значення перерахунку, що є комбінацією інших значень потужності у 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?
Сінджай

Ви перебуваєте на правильному шляху в редагуванні Sinjai, зберігаючи значення "unset" == 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

Щоб спростити перерахування прапора та зробити його, можливо, краще читати, уникаючи кратних, ми можемо скористатися бітовим зміщенням. (З гарної статті, що закінчується великою дискусією про прапори 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.