Por toda a minha vida, não me lembro como definir, excluir, alternar ou testar um pouco em um campo de bits. Ou não tenho certeza ou os confundo porque raramente preciso deles. Portanto, seria bom ter uma "folha de dicas".
Por exemplo:
flags = flags | FlagsEnum.Bit4; // Set bit 4.
ou
if ((flags & FlagsEnum.Bit4)) == FlagsEnum.Bit4) // Is there a less verbose way?
Você pode dar exemplos de todas as outras operações comuns, de preferência na sintaxe C # usando uma enumeração [Flags]?
Respostas:
Eu trabalhei mais nessas extensões - Você pode encontrar o código aqui
Eu escrevi alguns métodos de extensão que estendem o System.Enum que eu uso frequentemente ... Não estou afirmando que são à prova de balas, mas eles ajudaram ... Comentários removidos ...
Então eles são usados como o seguinte
fonte
HasFlag
métodoEnum
requer boxe.No .NET 4, agora você pode escrever:
fonte
FlagsEnum
seja um nome feio. :)[Flags]
enumerações devem ter nomes pluralizados, portanto, o nomeFlagsEnum
tem problemas ainda mais sérios que a feiura.O idioma é usar o operador bit a bit ou igual para definir bits:
Para limpar um pouco, o idioma é usar bit a bit e com negação:
Às vezes, você tem um deslocamento que identifica seu bit e, em seguida, o idioma é usá-lo combinado com o deslocamento para a esquerda:
fonte
@Desenhou
Observe que, exceto nos casos mais simples, o Enum.HasFlag acarreta uma penalidade de alto desempenho em comparação à gravação manual do código. Considere o seguinte código:
Mais de 10 milhões de iterações, o método de extensão HasFlags ocupa 4793 ms, em comparação com os 27 ms da implementação padrão em bits.
fonte
HasFlag
método envolve boxe / unboxing, o que explica essa diferença. Mas o custo é tão trivial (0,4µs) que, a menos que você esteja em um loop apertado, eu aceitaria a chamada declarativa da API mais legível (e menos provável de buggy) a qualquer dia.Infelizmente, as operações de enumeração de sinalizador do .NET são bastante limitadas. Na maioria das vezes, os usuários ficam com a lógica de operação bit a bit.
No .NET 4,
HasFlag
foi adicionado o métodoEnum
que ajuda a simplificar o código do usuário, mas infelizmente há muitos problemas com ele.HasFlag
não é seguro quanto ao tipo, pois aceita qualquer tipo de argumento de valor de enumeração, não apenas o tipo de enumeração especificado.HasFlag
é ambíguo se verifica se o valor possui todos ou alguns dos sinalizadores fornecidos pelo argumento de valor da enumeração. É tudo a propósito.HasFlag
é bastante lento, pois requer boxe, o que causa alocações e, portanto, mais coleta de lixo.Em parte devido ao suporte limitado do .NET para enumerações de flag, escrevi a biblioteca OSS Enums.NET, que aborda cada um desses problemas e facilita muito o tratamento de enumerações de flag.
Abaixo estão algumas das operações que ele fornece, juntamente com suas implementações equivalentes, usando apenas a estrutura .NET.
Combinar sinalizadores
.INTERNET
flags | otherFlags
Enums.NET
flags.CombineFlags(otherFlags)
Remover sinalizadores
.INTERNET
flags & ~otherFlags
Enums.NET
flags.RemoveFlags(otherFlags)
Sinalizadores comuns
.INTERNET
flags & otherFlags
Enums.NET
flags.CommonFlags(otherFlags)
Alternar sinalizadores
.INTERNET
flags ^ otherFlags
Enums.NET
flags.ToggleFlags(otherFlags)
Tem todas as bandeiras
.NET
(flags & otherFlags) == otherFlags
ouflags.HasFlag(otherFlags)
Enums.NET
flags.HasAllFlags(otherFlags)
Tem sinalizadores
.INTERNET
(flags & otherFlags) != 0
Enums.NET
flags.HasAnyFlags(otherFlags)
Obter sinalizadores
.INTERNET
Enums.NET
flags.GetFlags()
Estou tentando incorporar essas melhorias no .NET Core e, eventualmente, no .NET Framework completo. Você pode conferir minha proposta aqui .
fonte
Sintaxe C ++, assumindo que o bit 0 é LSB, assumindo que os sinalizadores não tenham assinatura longa:
Verifique se definido:
Verifique se não estiver definido:
Conjunto:
Claro:
Alternancia:
fonte
Para obter o melhor desempenho e zero lixo, use o seguinte:
fonte
Para testar um pouco, faça o seguinte: (supondo que sinalizadores seja um número de 32 bits)
Bit de teste:
(Se o bit 4 estiver definido, será verdadeiro) Alternar para trás (1 - 0 ou 0 - 1): Redefina o Bit 4 para Zero:fonte
~0x08
em vez de0xFFFFFFF7
... (a máscara real para 0x8)Isso foi inspirado pelo uso de Sets como indexadores no Delphi, quando:
fonte
As operações C ++ são: & | ^ ~ (para e, ou, xor e não operações bit a bit). Também são interessantes >> e <<, que são operações de deslocamento de bits.
Portanto, para testar um bit sendo definido em um sinalizador, você usaria: se (flags & 8) // testes o bit 4 foi definido
fonte