Допустим, у меня есть следующее
int susan = 2; //0010
int bob = 4; //0100
int karen = 8; //1000
и я передаю 10 (8 + 2) в качестве параметра методу, и я хочу декодировать это, чтобы означать Сьюзан и Карен
Я знаю, что 10 это 1010
но как я могу сделать некоторую логику, чтобы увидеть, проверяется ли определенный бит, как в
if (condition_for_karen) // How to quickly check whether effective karen bit is 1
Прямо сейчас все, о чем я могу думать, это проверить, является ли число, которое я передал,
14 // 1110
12 // 1100
10 // 1010
8 // 1000
Когда у меня есть большее количество фактических битов в моем реальном сценарии, это кажется непрактичным. Как лучше использовать маску, чтобы просто проверить, выполняю ли я условие только для Карен?
Я могу думать о смещении влево, затем назад, затем вправо, а затем обратно, чтобы очистить биты, отличные от тех, которые меня интересуют, но это также кажется слишком сложным.
Ответы:
Традиционный способ сделать это - использовать
Flags
атрибут вenum
:Затем вы должны проверить конкретное имя следующим образом:
Логические побитовые комбинации сложно запомнить, поэтому я облегчаю себе жизнь с помощью
FlagsHelper
класса *:Это позволило бы мне переписать приведенный выше код как:
Обратите внимание: я также мог бы добавить
Karen
в набор, выполнив следующие действия:И я мог бы удалить
Susan
аналогичным образом:* Как Поргес отметил, эквивалент этого
IsSet
метода выше уже существует в .NET 4.0:Enum.HasFlag
.Set
ИUnset
методы , кажется , не имеет аналогов, хотя; так что я бы все же сказал, что у этого класса есть некоторые достоинства.Примечание. Использование перечислений - это обычный способ решения этой проблемы. Вы можете полностью перевести весь приведенный выше код на использование целых чисел, и он будет работать так же хорошо.
источник
(names & Names.Susan) == Names.Susan
, для чего не требуетсяNone
.None
значение для всех моих перечислений, так как считаю, что это удобно во многих сценариях.var susanIsIncluded = names.HasFlag(Names.Susan);
Set
метода. Так что, я бы сказал, что вспомогательные методы, по крайней мере, не совсем бесполезны.)names.HasFlag(Names.Susan)
похоже на то,(names & Names.Susan) == Names.Susan
что не всегда нравится(names & Names.Susan) != Names.None
. Например , если вы будете проверять , еслиnames.HasFlag(Names.none)
илиnames.HasFlag(Names.Susan|Names.Karen)
Поразрядное "и" замаскирует все, кроме бит, который "представляет" Карен. Пока каждый человек представлен одной битовой позицией, вы можете проверить несколько человек с помощью простого:
источник
Я включил сюда пример, который демонстрирует, как вы можете сохранить маску в столбце базы данных как int и как вы можете восстановить маску позже:
источник
Чтобы комбинировать битовые маски, вы хотите использовать побитовое или . В тривиальном случае, когда каждое объединенное вами значение имеет ровно 1 бит (как в вашем примере), это эквивалентно их добавлению. Однако, если у вас есть перекрывающиеся биты, или их использование изящно обрабатывает случай.
Чтобы декодировать битовые маски, вы и ваше значение с помощью маски, например:
источник
Простой способ:
Для установки флагов используйте логический оператор «или»
|
:И чтобы проверить, включен ли флаг, используйте
HasFlag
:источник
HasFlag()
и[Flags]
не был приведен в других ответах.Еще одна действительно веская причина использовать битовую маску вместо отдельных bools - это, как веб-разработчик, когда мы интегрируем один веб-сайт с другим, нам часто нужно отправлять параметры или флаги в строке запроса. Поскольку все ваши флаги являются двоичными, это значительно упрощает использование одного значения в качестве битовой маски, чем отправку нескольких значений в виде логических значений. Я знаю, что есть другие способы отправки данных (GET, POST и т. Д.), Но для нечувствительных элементов в большинстве случаев достаточно простого параметра в строке запроса. Попробуйте отправить 128 значений типа bool в строке запроса для связи с внешним сайтом. Это также дает дополнительную возможность не нажимать ограничение на строки запроса URL в браузерах.
источник