mascara - Usando una máscara de bits en C#
mascara de bits en c (5)
He incluido un ejemplo aquí que demuestra cómo se puede almacenar la máscara en una columna de base de datos como un int, y cómo se restablecería la máscara más adelante:
public enum DaysBitMask { Mon=0, Tues=1, Wed=2, Thu = 4, Fri = 8, Sat = 16, Sun = 32 }
DaysBitMask mask = DaysBitMask.Sat | DaysBitMask.Thu;
bool test;
if ((mask & DaysBitMask.Sat) == DaysBitMask.Sat)
test = true;
if ((mask & DaysBitMask.Thu) == DaysBitMask.Thu)
test = true;
if ((mask & DaysBitMask.Wed) != DaysBitMask.Wed)
test = true;
// Store the value
int storedVal = (int)mask;
// Reinstate the mask and re-test
DaysBitMask reHydratedMask = (DaysBitMask)storedVal;
if ((reHydratedMask & DaysBitMask.Sat) == DaysBitMask.Sat)
test = true;
if ((reHydratedMask & DaysBitMask.Thu) == DaysBitMask.Thu)
test = true;
if ((reHydratedMask & DaysBitMask.Wed) != DaysBitMask.Wed)
test = true;
Digamos que tengo lo siguiente
int susan = 2; //0010
int bob = 4; //0100
int karen = 8; //1000
y paso 10 (8 + 2) como un parámetro a un método y quiero decodificar esto para que signifique susan y karen
Sé que 10 es 1010
pero ¿cómo puedo hacer algo de lógica para ver si un bit específico está marcado como en
if (condition_for_karen) // How to quickly check whether effective karen bit is 1
En este momento todo lo que puedo pensar es verificar si el número que pasé es
14 // 1110
12 // 1100
10 // 1010
8 // 1000
Cuando tengo un número mayor de bits reales en mi escenario del mundo real, esto parece poco práctico, ¿cuál es la mejor manera de usar una máscara para comprobar si cumplo o no con la condición de solo karen?
Puedo pensar en desplazarme hacia la izquierda y luego hacia atrás, y luego hacia atrás y hacia atrás para borrar otras partes que no sean la que me interesa, pero esto también parece demasiado complejo.
La forma tradicional de hacerlo es usar el atributo Flags
en una enum
:
[Flags]
public enum Names
{
None = 0,
Susan = 1,
Bob = 2,
Karen = 4
}
Luego verificarías un nombre particular de la siguiente manera:
Names names = Names.Susan | Names.Bob;
// evaluates to true
bool susanIsIncluded = (names & Names.Susan) != Names.None;
// evaluates to false
bool karenIsIncluded = (names & Names.Karen) != Names.None;
Las combinaciones lógicas en bits pueden ser difíciles de recordar, así que me FlagsHelper
vida con una clase FlagsHelper
*:
// The casts to object in the below code are an unfortunate necessity due to
// C#''s restriction against a where T : Enum constraint. (There are ways around
// this, but they''re outside the scope of this simple illustration.)
public static class FlagsHelper
{
public static bool IsSet<T>(T flags, T flag) where T : struct
{
int flagsValue = (int)(object)flags;
int flagValue = (int)(object)flag;
return (flagsValue & flagValue) != 0;
}
public static void Set<T>(ref T flags, T flag) where T : struct
{
int flagsValue = (int)(object)flags;
int flagValue = (int)(object)flag;
flags = (T)(object)(flagsValue | flagValue);
}
public static void Unset<T>(ref T flags, T flag) where T : struct
{
int flagsValue = (int)(object)flags;
int flagValue = (int)(object)flag;
flags = (T)(object)(flagsValue & (~flagValue));
}
}
Esto me permitiría reescribir el código anterior como:
Names names = Names.Susan | Names.Bob;
bool susanIsIncluded = FlagsHelper.IsSet(names, Names.Susan);
bool karenIsIncluded = FlagsHelper.IsSet(names, Names.Karen);
Tenga en cuenta que también podría agregar Karen
al conjunto haciendo esto:
FlagsHelper.Set(ref names, Names.Karen);
Y podría eliminar a Susan
de una manera similar:
FlagsHelper.Unset(ref names, Names.Susan);
* Como señaló Porges, ya existe un equivalente del método IsSet
anterior en .NET 4.0: Enum.HasFlag
. Sin embargo, los métodos Set
y Unset
no parecen tener equivalentes; así que aún diría que esta clase tiene algún mérito.
Nota: Usar enums es solo la forma convencional de abordar este problema. Puedes traducir totalmente todo el código anterior para usar ints en su lugar y funcionará igual de bien.
Otra buena razón para usar una máscara de bits contra bools individuales es como desarrollador web, cuando se integra un sitio web a otro, con frecuencia necesitamos enviar parámetros o indicadores en la cadena de consulta. Siempre que todos sus indicadores sean binarios, es mucho más simple usar un solo valor como máscara de bits que enviar valores múltiples como bools. Sé que hay otras formas de enviar datos (GET, POST, etc.), pero un parámetro simple en la cadena de consulta es la mayor parte del tiempo suficiente para elementos no sensibles. Intente enviar 128 valores de bool en una cadena de consulta para comunicarse con un sitio externo. Esto también le da la capacidad adicional de no presionar el límite de las cadenas de direcciones url en los navegadores.
Para combinar las máscaras de bits que desee usar en modo bit o . En el caso trivial en el que cada valor que combine tiene exactamente 1 bit activado (como su ejemplo), es equivalente a agregarlos. Sin embargo, si tiene trozos superpuestos, o ''ellos maneja el caso con gracia.
Para decodificar las máscaras de bits usted y su valor con una máscara, haga lo siguiente:
if(val & (1<<1)) SusanIsOn();
if(val & (1<<2)) BobIsOn();
if(val & (1<<3)) KarenIsOn();
if ( ( param & karen ) == karen )
{
// Do stuff
}
El bit ''y'' enmascarará todo, excepto el bit que "representa" a Karen. Mientras que cada persona esté representada por una posición de un solo bit, puede verificar varias personas con un simple:
if ( ( param & karen ) == karen )
{
// Do Karen''s stuff
}
if ( ( param & bob ) == bob )
// Do Bob''s stuff
}