¿Cómo comparar banderas en C #?

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?

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); } 
 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 

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 statement 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 

@ 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.

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); 
 if((testItem & FlagTest.Flag1) == FlagTest.Flag1) { ... } 

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.

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)

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 syntax que necesitas.

es decir

 public class FlagTestCompare { public static bool Compare(this FlagTest myFlag, FlagTest condition) { return ((myFlag & condition) == condition); } } 

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){ //.. }