Digamos que tenho o seguinte
int susan = 2; //0010
int bob = 4; //0100
int karen = 8; //1000
e eu passo 10 (8 + 2) como um parâmetro para um método e quero decodificar isso para significar susan e karen
Eu sei que 10 é 1010
mas como posso fazer alguma lógica para ver se um bit específico é verificado como em
if (condition_for_karen) // How to quickly check whether effective karen bit is 1
No momento, tudo que consigo pensar é verificar se o número que passei é
14 // 1110
12 // 1100
10 // 1010
8 // 1000
Quando eu tenho um número maior de bits reais em meu cenário do mundo real, isso parece impraticável. Qual é a melhor maneira de usar uma máscara para apenas verificar se eu atendo ou não a condição apenas para karen?
Posso pensar em mudar para a esquerda e depois voltar para a direita e depois voltar para partes claras que não sejam aquelas em que estou interessado, mas isso também parece excessivamente complexo.
Respostas:
A maneira tradicional de fazer isso é usar o
Flags
atributo emenum
:Em seguida, você deve verificar um nome específico da seguinte maneira:
Combinações lógicas bit a bit podem ser difíceis de lembrar, então eu facilito minha vida com uma
FlagsHelper
aula *:Isso me permitiria reescrever o código acima como:
Observe que eu também poderia adicionar
Karen
ao conjunto fazendo o seguinte:E eu poderia remover de
Susan
forma semelhante:* Como Porges salientado, um equivalente do
IsSet
método acima já existe em .NET 4.0:Enum.HasFlag
. Os métodosSet
eUnset
não parecem ter equivalentes, no entanto; então ainda diria que esta aula tem algum mérito.Observação: usar enums é apenas a maneira convencional de resolver esse problema. Você pode traduzir totalmente todo o código acima para usar ints e funcionará tão bem.
fonte
(names & Names.Susan) == Names.Susan
, o que não requer umNone
.None
valor para todos os meus enums, pois acho que acaba sendo conveniente em muitos cenários.var susanIsIncluded = names.HasFlag(Names.Susan);
Set
método. Então, eu diria que os métodos auxiliares pelo menos não são totalmente inúteis.)names.HasFlag(Names.Susan)
é semelhante ao(names & Names.Susan) == Names.Susan
que nem sempre é semelhante(names & Names.Susan) != Names.None
. Por exemplo, se você verificar senames.HasFlag(Names.none)
ounames.HasFlag(Names.Susan|Names.Karen)
O bit a bit 'e' mascarará tudo, exceto o bit que "representa" Karen. Contanto que cada pessoa seja representada por uma única posição de bit, você pode marcar várias pessoas com um simples:
fonte
Incluí um exemplo aqui que demonstra como você pode armazenar a máscara em uma coluna do banco de dados como um int e como você restauraria a máscara posteriormente:
fonte
Para combinar bitmasks, você deseja usar bitwise- ou . No caso trivial onde cada valor que você combina tem exatamente 1 bit ativado (como seu exemplo), é equivalente a adicioná-los. Se você tiver bits sobrepostos, no entanto, ou se eles lidarem com o caso normalmente.
Para decodificar as máscaras de bits você e seu valor com uma máscara, assim:
fonte
Jeito fácil:
Para definir os sinalizadores, use o operador lógico "ou"
|
:E para verificar se um sinalizador está incluído, use
HasFlag
:fonte
HasFlag()
e[Flags]
não foi fornecido em outras respostas.Uma outra razão muito boa para usar um bitmask em vez de bools individuais é, como desenvolvedor web, ao integrar um site a outro, frequentemente precisamos enviar parâmetros ou sinalizadores na string de consulta. Desde que todos os seus sinalizadores sejam binários, é muito mais simples usar um único valor como uma máscara de bits do que enviar vários valores como bools. Eu sei que existem outras maneiras de enviar dados (GET, POST, etc.), mas um parâmetro simples na string de consulta é na maioria das vezes suficiente para itens não sensíveis. Tente enviar 128 valores bool em uma string de consulta para se comunicar com um site externo. Isso também oferece a capacidade adicional de não ultrapassar o limite de strings de consulta de URL em navegadores
fonte