programacion - ¿Cómo comparar banderas en C#?
metodo bandera c# (11)
Tengo una enumeración de la bandera a continuación.
[Flags]
public enum FlagTest
{
None = 0x0,
Flag1 = 0x1,
Flag2 = 0x2,
Flag3 = 0x4
}
No puedo hacer que la sentencia if se evalúe como verdadera.
FlagTest testItem = FlagTest.Flag1 | FlagTest.Flag2;
if (testItem == FlagTest.Flag1)
{
// Do something,
// however This is never true.
}
¿Cómo puedo hacer esto verdad?
@ phil-devaney
Tenga en cuenta que, excepto en los casos más simples, Enum.HasFlag conlleva una gran penalización de rendimiento en comparación con escribir el código manualmente. Considera el siguiente código:
[Flags]
public enum TestFlags
{
One = 1,
Two = 2,
Three = 4,
Four = 8,
Five = 16,
Six = 32,
Seven = 64,
Eight = 128,
Nine = 256,
Ten = 512
}
class Program
{
static void Main(string[] args)
{
TestFlags f = TestFlags.Five; /* or any other enum */
bool result = false;
Stopwatch s = Stopwatch.StartNew();
for (int i = 0; i < 10000000; i++)
{
result |= f.HasFlag(TestFlags.Three);
}
s.Stop();
Console.WriteLine(s.ElapsedMilliseconds); // *4793 ms*
s.Restart();
for (int i = 0; i < 10000000; i++)
{
result |= (f & TestFlags.Three) != 0;
}
s.Stop();
Console.WriteLine(s.ElapsedMilliseconds); // *27 ms*
Console.ReadLine();
}
}
Más de 10 millones de iteraciones, el método de extensión HasFlags toma la friolera de 4793 ms, en comparación con los 27 ms para la implementación bit a bit estándar.
Configuré un método de extensión para hacerlo: pregunta relacionada .
Básicamente:
public static bool IsSet( this Enum input, Enum matchTo )
{
return ( Convert.ToUInt32( input ) & Convert.ToUInt32( matchTo ) ) != 0;
}
Entonces puedes hacer:
FlagTests testItem = FlagTests.Flag1 | FlagTests.Flag2;
if( testItem.IsSet ( FlagTests.Flag1 ) )
//Flag1 is set
Por cierto, la convención que uso para enumeraciones es singular para estándar, plural para banderas. De esta forma sabrá a partir del nombre enum si puede contener múltiples valores.
En .NET 4 hay un nuevo método Enum.HasFlag . Esto te permite escribir:
if ( testItem.HasFlag( FlagTest.Flag1 ) )
{
// Do Stuff
}
que es mucho más legible, IMO.
La fuente .NET indica que esto realiza la misma lógica que la respuesta aceptada:
public Boolean HasFlag(Enum flag) {
if (!this.GetType().IsEquivalentTo(flag.GetType())) {
throw new ArgumentException(
Environment.GetResourceString(
"Argument_EnumTypeDoesNotMatch",
flag.GetType(),
this.GetType()));
}
ulong uFlag = ToUInt64(flag.GetValue());
ulong uThis = ToUInt64(GetValue());
// test predicate
return ((uThis & uFlag) == uFlag);
}
En cuanto a la edición. No puedes hacer que sea verdad. Te sugiero que envuelvas lo que quieras en otra clase (o método de extensión) para acercarte a la sintaxis que necesitas.
es decir
public class FlagTestCompare
{
public static bool Compare(this FlagTest myFlag, FlagTest condition)
{
return ((myFlag & condition) == condition);
}
}
Para aquellos que tienen problemas para visualizar lo que está sucediendo con la solución aceptada (que es esto),
if ((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
// Do stuff.
}
testItem
(según la pregunta) se define como,
testItem
= flag1 | flag2
= 001 | 010
= 011
Luego, en la declaración if, el lado izquierdo de la comparación es,
(testItem & flag1)
= (011 & 001)
= 001
Y la instrucción if completa (que se evalúa como verdadera si se establece testItem
en testItem
),
(testItem & flag1) == flag1
= (001) == 001
= true
Para operaciones de bits, necesita usar operadores bit a bit.
Esto debería funcionar:
if ((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
// Do something,
// however This is never true.
}
Editar: se corrigió mi comprobación si - volví a mis modos de C / C ++ (gracias a Ryan Farley por señalarlo)
Prueba esto:
if ((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
// do something
}
Básicamente, su código pregunta si tener ambas banderas configuradas es lo mismo que tener una bandera configurada, lo cual es obviamente falso. El código de arriba dejará solo el bit de Flag1 establecido si está configurado en absoluto, luego compara este resultado con Flag1.
Un consejo más ... Nunca hagas la comprobación binaria estándar con la bandera cuyo valor es "0". Su cheque en esta bandera siempre será verdadero.
[Flags]
public enum LevelOfDetail
{
[EnumMember(Value = "FullInfo")]
FullInfo=0,
[EnumMember(Value = "BusinessData")]
BusinessData=1
}
Si el parámetro de entrada de verificación binario contra FullInfo, obtiene:
detailLevel = LevelOfDetail.BusinessData;
bool bPRez = (detailLevel & LevelOfDetail.FullInfo) == LevelOfDetail.FullInfo;
bPRez siempre será cierto como NADA y 0 siempre == 0.
En su lugar, simplemente debe verificar que el valor de la entrada sea 0:
bool bPRez = (detailLevel == LevelOfDetail.FullInfo);
incluso sin [Banderas], podrías usar algo como esto
if((testItem & (FlagTest.Flag1 | FlagTest.Flag2 ))!=0){
//..
}
o si tiene un enum de valor cero
if((testItem & (FlagTest.Flag1 | FlagTest.Flag2 ))!=FlagTest.None){
//..
}
if ((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
// Do something
}
(testItem & FlagTest.Flag1)
es una operación Y a nivel de bit.
FlagTest.Flag1
es equivalente a 001
con la enumeración de OP. Ahora digamos testItem
tiene Flag1 y Flag2 (por lo que es bitwise 101
):
001
&101
----
001 == FlagTest.Flag1
if((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
...
}