¿Cómo eliminar un elemento para una enumeración OR?

181

Tengo una enumeración como:

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

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

¿Cómo podría eliminar el color azul de los colores variables?

Blankman
fuente
2
Pequeña nota: una enumeración bit a bit en C # debería obtener el atributo [Flags] por encima de él.
Nyerguds
@Nyerguds, ¿podría explicar por qué debería obtener el atributo?
etano
Ofrece una mejor compatibilidad con IntelliSense durante la depuración, porque reconoce valores de enumeración desconocidos como combinaciones de valores existentes.
Nyerguds
1
También proporciona un .ToString()valor más significativo . por ejemplo, en RED, BLUE, YELLOWlugar de 22.
Sinjai

Respuestas:

331

Lo necesitas &con el ~(complemento) de 'AZUL'.

El operador del complemento esencialmente invierte o 'voltea' todos los bits para el tipo de datos dado. Como tal, si usa el ANDoperador (& ) con algún valor (llamemos a ese valor 'X') y el complemento de uno o más bits de conjunto (llamemos a esos bits Qy su complemento ~Q), la instrucción X & ~Qborra todos los bits que se establecieron en Qde Xy devuelve el resultado.

Entonces, para eliminar o borrar los BLUEbits, use la siguiente declaración:

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

También puede especificar varios bits para borrar, de la siguiente manera:

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

o alternativamente ...

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

Entonces para resumir:

  • X | Q establece bit (s) Q
  • X & ~Q borra bit (s) Q
  • ~X voltea / invierte todos los bits en X
SLaks
fuente
1
así que si quisiera eliminar más, simplemente haría: & = ~ Blah.BLUE & ~ Blah.Green?
Blankman
11
Mejor escolors &= ~( Blah.BLUE | Blah.GREEN );
par
2
qué inusual, estaba pensando cómo hacer esto una vez más hace unos 5 días :) ¡Esta pregunta se actualizó en mi bandeja de entrada y listo!
Blankman
Gran respuesta. Sin embargo, creo que te refieres a "& =" not "= &" en tus ejemplos de código;)
Dave Jellison
48

Las otras respuestas son correctas, pero para eliminar específicamente el azul de lo anterior, escribiría:

colors &= ~Blah.BLUE;
par
fuente
9

And not eso...............................

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

Pensé que esto podría ser útil para otras personas que tropezaron aquí como yo.

Tenga cuidado al manejar cualquier valor de enumeración que pueda establecer para tener un valor == 0 (a veces puede ser útil tener un estado Desconocido o Inactivo para una enumeración). Causa problemas al confiar en estas operaciones de manipulación de bits.

También cuando tiene valores de enumeración que son combinaciones de otra potencia de 2 valores, p. Ej.

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

En estos casos, un método de extensión como este podría 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;
}

Y también el método IsSet equitativo que maneja los valores combinados (aunque de una manera un poco 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
fuente
Me parece que la solución más simple es evitar usar 0 como bandera. Por lo general, configuro enums de bandera como este . No veo cómo se beneficia al especificar un estado con un valor de 0. Después de todo, el punto es que puede preocuparse por los nombres amigables para el programador ( Red, Blue, Green) en lugar de los valores subyacentes, ¿sí?
Sinjai
Tener una noneo unknowno unsetentrada con el valor == 0 es en muchos casos una buena cosa ya que no siempre se puede asumir que un valor particular es el conjunto predeterminado, por ejemplo, si sólo tiene rojo, azul y verde que se van a tener como Rojo el valor predeterminado (por ejemplo, el valor que tiene la enumeración cuando se crea o el usuario no lo ha modificado)
Sverrir Sigmundarson
¿No puede simplemente tener Unset = 1 y usar 1, 2, 4, 8como sus valores? Si necesita un valor predeterminado, para eso están los constructores predeterminados, ¿verdad? Me gusta mantener las cosas lo más explícitas posible en aras de la legibilidad, y confiar en una enumeración predeterminada default(int)( 0) cuando no se le da un valor, a pesar de ser información presumiblemente conocida por cualquier desarrollador, de todos modos es demasiado astuto para mis gustos. Editar: Oh, espera, ¿hacer Unset = 0 evitaría que algo sea Unset | Blue?
Sinjai
Está en el camino correcto en su edición Sinjai, manteniendo el valor no establecido == 0 en realidad resuelve algunos problemas. Uno es lo que mencionó y otro es si desarma todos los valores de enumeración, entonces no necesita un caso especial para "Desarmar" si está puesto a cero. (por ejemplo, si Red & ~Redtermina con cero, entonces tendría que tener un código para verificar este caso y asignar explícitamente el Unsetvalor)
Sverrir Sigmundarson
Gotcha ¿A menudo te encuentras lidiando con banderas no configuradas?
Sinjai
2

¿Qué pasa con xor (^)?

Dado que la BANDERA que está tratando de eliminar está allí, funcionará ... si no, tendrá que usar un &.

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
fuente
1

Para simplificar la enumeración de la bandera y hacer que sea mejor leer evitando los múltiplos, podemos usar el desplazamiento de bits. (De un buen artículo Terminando el Gran Debate sobre Enum Flags )

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

y también para borrar todos los bits

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

Puedes usar esto:

colors &= ~Blah.RED; 
Majid gharaei
fuente