Como remover um item para uma enumeração OR'd?

181

Eu tenho um enum como:

public enum Blah
{
    RED = 2,
    BLUE = 4,
    GREEN = 8,
    YELLOW = 16
}

Blah colors = Blah.RED | Blah.BLUE | Blah.YELLOW;

Como remover a cor azul das cores variáveis?

Blankman
fonte
2
Nota pequena: uma enumeração bit a bit em C # deve obter o atributo [Flags] acima dele.
Nyerguds
@ Nyerguds, você poderia explicar por que deveria obter o atributo?
19717
Ele oferece um suporte melhor ao IntelliSense durante a depuração, porque reconhece valores de enumeração desconhecidos como combinações de valores existentes.
Nyerguds
1
Ele também fornece um .ToString()valor mais significativo . por exemplo, em RED, BLUE, YELLOWvez de 22.
Sinjai

Respostas:

331

Você precisa fazer &isso com o ~(complemento) de 'AZUL'.

O operador complemento essencialmente reverte ou 'vira' todos os bits para o tipo de dados fornecido. Assim, se você usar o ANDoperador ( &) com algum valor (vamos chamar esse valor de 'X') e o complemento de um ou mais bits definidos (vamos chamar esses bits Qe seu complemento ~Q), a instruçãoX & ~Q limpará todos os bits que foram configurados em Qfrom Xe retorna o resultado.

Portanto, para remover ou limpar os BLUEbits, use a seguinte instrução:

colorsWithoutBlue = colors & ~Blah.BLUE
colors &= ~Blah.BLUE // This one removes the bit from 'colors' itself

Você também pode especificar vários bits para limpar, da seguinte maneira:

colorsWithoutBlueOrRed = colors & ~(Blah.BLUE | Blah.RED)
colors &= ~(Blah.BLUE | Blah.RED) // This one removes both bits from 'colors' itself

ou alternadamente ...

colorsWithoutBlueOrRed = colors & ~Blah.BLUE & ~Blah.RED
colors &= ~Blah.BLUE & ~Blah.RED // This one removes both bits from 'colors' itself

Então, para resumir:

  • X | Q define bit (s) Q
  • X & ~Q limpa bit (s) Q
  • ~X vira / inverte todos os bits X
SLaks
fonte
1
então, se eu quisesse remover mais, faria: & = ~ Blah.BLUE & ~ Blah.Green?
Blankman
11
Melhor écolors &= ~( Blah.BLUE | Blah.GREEN );
par
2
que incomum, eu estava pensando em como fazer isso mais uma vez cerca de 5 dias atrás :) Esta pergunta foi atualizada na minha caixa de entrada e pronto!
quer
Ótima resposta. Eu acho que você quer dizer "& =" não "= &" em seus exemplos de código no entanto;)
Dave Jellison
48

As outras respostas estão corretas, mas para remover especificamente o azul das opções acima, você deve escrever:

colors &= ~Blah.BLUE;
par
fonte
9

And not isto...............................

Blah.RED | Blah.YELLOW == 
   (Blah.RED | Blah.BLUE | Blah.YELLOW) & ~Blah.BLUE;
Daniel A. White
fonte
7

Achei que isso poderia ser útil para outras pessoas que tropeçaram aqui como eu.

Tenha cuidado ao lidar com quaisquer valores de enumeração que você pode definir para ter um valor == 0 (às vezes pode ser útil ter um estado Desconhecido ou Inativo para uma enumeração). Causa problemas ao confiar nessas operações de manipulação de bits.

Além disso, quando você tem valores de enumeração que são combinações de outra potência de 2 valores, por exemplo,

public enum Colour
{
    None = 0,  // default value
    RED = 2,
    BLUE = 4,
    GREEN = 8,
    YELLOW = 16,
    Orange = 18  // Combined value of RED and YELLOW
}

Nesses casos, um método de extensão como esse pode ser útil:

public static Colour UnSet(this Colour states, Colour state)
{
    if ((int)states == 0)
        return states;

    if (states == state)
        return Colour.None;

    return states & ~state;
}

E também o método IsSet equivalente que lida com os valores combinados (embora de um jeito meio hacky)

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;
}
Sverrir Sigmundarson
fonte
Parece-me que a solução mais simples é evitar o uso de 0 como sinalizador. Eu normalmente configuro enums de flag como este . Não vejo como você se beneficia ao especificar um estado com um valor 0. Afinal, o ponto principal é que você pode se preocupar com os nomes amigáveis ​​ao programador ( Red, Blue, Green) e não com os valores subjacentes, sim?
Sinjai
Ter uma entrada noneou unknownou unsetcom valor == 0 é, em muitos casos, uma coisa boa, pois nem sempre é possível assumir que um valor específico é o conjunto padrão; por exemplo, se você tiver apenas vermelho, azul e verde, terá vermelho como o valor padrão (por exemplo, o valor que o enum tem quando é criado ou o usuário não tenha modificado?)
Sverrir Sigmundarson
Você não pode simplesmente ter Unset = 1 e usar 1, 2, 4, 8como seus valores? Se você precisa de um valor padrão, é para isso que servem os construtores padrão, certo? Eu gosto de manter as coisas o mais explícitas possível, por questões de legibilidade, e confiar em um enum para usar como padrão default(int)( 0) quando não receber um valor, apesar de serem informações presumivelmente conhecidas por qualquer desenvolvedor, é muito sorrateiro para o meu gosto. Edit: Oh espere, fazer Unset = 0 impedir que algo seja Unset | Blue?
Sinjai
Você está no caminho certo em sua edição Sinjai, mantendo o valor não definido == 0 na verdade resolve alguns problemas. Um sendo o que você mencionou e outro sendo, se você desarmar todos os valores de enumeração, não precisará de um caso especial para "Desativar" se estiver definido como zero. (por exemplo, se você Red & ~Red, em seguida, você acabar com zero, você então tem que ter código para verificar para este caso e atribuir explicitamente o Unsetvalor)
Sverrir Sigmundarson
Peguei vocês. Você costuma lidar com bandeiras não definidas?
Sinjai 02/0318
2

E o xor (^)?

Dado que o FLAG que você está tentando remover está lá, ele funcionará. Caso contrário, você precisará usar um &.

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;
user4509414
fonte
1

Para simplificar a enumeração do sinalizador e facilitar a leitura, evitando múltiplos, podemos usar a mudança de bits. (De um bom artigo Terminando o Grande Debate sobre Bandeiras Enum )

[FLAG]
Enum Blah
{
   RED = 1,
   BLUE = 1 << 1,
   GREEN = 1 << 2,
   YELLOW = 1 << 3
}

e também para limpar todos os bits

private static void ClearAllBits()
{
    colors = colors & ~colors;
}
Matt Allen
fonte
0

Você pode usar isto:

colors &= ~Blah.RED; 
Majid gharaei
fonte