Используя битовую маску в C#

Скажем, у меня есть следующее

int susan = 2; //0010
int bob = 4; //0100
int karen = 8; //1000

и я передаю 10 (8 + 2) в качестве параметра к методу, и я хочу декодировать это для значения susan и karen

Я знаю, что 10 1010

но как я могу сделать некоторую логику, чтобы видеть, проверяется ли определенный бит как в

if (condition_for_karen) // How to quickly check whether effective karen bit is 1

Прямо сейчас все, о чем я могу думать, должно проверить, является ли число, которое я передал,

14 // 1110
12 // 1100
10 // 1010
8 //  1000

Когда у меня есть большее число фактических битов в моем сценарии реального мира, это кажется непрактичным, что лучший путь использует маску, чтобы просто проверить, удовлетворяю ли я условию для просто karen?

Я могу думать о смещении, оставленном затем обратно затем смещение прямо тогда назад для очистки битов кроме того, которым я интересуюсь, но это также кажется чрезмерно сложным.

90
задан Matt 16 July 2010 в 02:08
поделиться

3 ответа

Традиционный способ сделать это - использовать атрибут Flags в перечислении :

[Flags]
public enum Names
{
    None = 0,
    Susan = 1,
    Bob = 2,
    Karen = 4
}

Затем вы должны проверить конкретное имя следующим образом:

Names names = Names.Susan | Names.Bob;

// evaluates to true
bool susanIsIncluded = (names & Names.Susan) != Names.None;

// evaluates to false
bool karenIsIncluded = (names & Names.Karen) != Names.None;

Логические побитовые комбинации сложно запомнить, поэтому я облегчил себе жизнь с помощью класса FlagsHelper *:

// The casts to object in the below code are an unfortunate necessity due to
// C#'s restriction against a where T : Enum constraint. (There are ways around
// this, but they're outside the scope of this simple illustration.)
public static class FlagsHelper
{
    public static bool IsSet<T>(T flags, T flag) where T : struct
    {
        int flagsValue = (int)(object)flags;
        int flagValue = (int)(object)flag;

        return (flagsValue & flagValue) != 0;
    }

    public static void Set<T>(ref T flags, T flag) where T : struct
    {
        int flagsValue = (int)(object)flags;
        int flagValue = (int)(object)flag;

        flags = (T)(object)(flagsValue | flagValue);
    }

    public static void Unset<T>(ref T flags, T flag) where T : struct
    {
        int flagsValue = (int)(object)flags;
        int flagValue = (int)(object)flag;

        flags = (T)(object)(flagsValue & (~flagValue));
    }
}

Это позволило бы мне переписать приведенный выше код как:

Names names = Names.Susan | Names.Bob;

bool susanIsIncluded = FlagsHelper.IsSet(names, Names.Susan);

bool karenIsIncluded = FlagsHelper.IsSet(names, Names.Karen);

Примечание. Я мог бы также добавить Карен в набор, выполнив следующие действия:

FlagsHelper.Set(ref names, Names.Karen);

И я мог бы удалить Сьюзан аналогичным образом:

FlagsHelper.Unset(ref names, Names.Susan);

* Как указал Поргес, эквивалент из приведенного выше метода IsSet уже существует в .NET 4.0: Enum.HasFlag . Однако методы Set и Unset , похоже, не имеют эквивалентов; так что я бы все же сказал, что у этого класса есть некоторые достоинства.


Примечание. Использование перечислений - это просто обычный способ решения этой проблемы. Вы можете полностью перевести весь приведенный выше код на использование целых чисел, и он будет работать так же хорошо.

184
ответ дан 24 November 2019 в 06:59
поделиться
if ( ( param & karen ) == karen )
{
  // Do stuff
}

Поразрядное «и» замаскирует все, кроме бита, который «представляет» Карен. Пока каждый человек представлен одной битовой позицией, вы можете проверить несколько людей с помощью простого:

if ( ( param & karen ) == karen )
{
  // Do Karen's stuff
}
if ( ( param & bob ) == bob )
  // Do Bob's stuff
}
20
ответ дан 24 November 2019 в 06:59
поделиться

Чтобы комбинировать битовые маски, которые вы хотите использовать побитово - или ]. В тривиальном случае, когда каждое объединенное вами значение имеет ровно 1 бит (как в вашем примере), это эквивалентно их добавлению. Однако, если у вас есть перекрывающиеся биты, или 'их обработка корректно обрабатывает регистр.

Чтобы декодировать битовые маски, вы и ваше значение с помощью маски, например:

if(val & (1<<1)) SusanIsOn();
if(val & (1<<2)) BobIsOn();
if(val & (1<<3)) KarenIsOn();
7
ответ дан 24 November 2019 в 06:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: