How to check if any flags of a flag combination are set?

匿名 (未验证) 提交于 2019-12-03 02:12:02

问题:

Let's say I have this enum:

[Flags] enum Letters {      A = 1,      B = 2,      C = 4,      AB = A | B,      All = A | B | C, } 

To check if for example AB is set I can do this:

if((letter & Letters.AB) == Letters.AB) 

Is there a simpler way to check if any of the flags of a combined flag constant are set than the following?

if((letter & Letters.A) == Letters.A || (letter & Letters.B) == Letters.B) 

Could you for example swap the & with something?

Not too stable when it comes to binary stuff like this...

回答1:

If you want to know if letter has any of the letters in AB you must use the AND & operator. Something like:

if ((letter & Letters.AB) != 0) {     // Some flag (A,B or both) is enabled } else {     // None of them are enabled } 


回答2:

In .NET 4 you can use the Enum.HasFlag method :

using System;  [Flags] public enum Pet {    None = 0,    Dog = 1,    Cat = 2,    Bird = 4,    Rabbit = 8,    Other = 16 }  public class Example {    public static void Main()    {       // Define three families: one without pets, one with dog + cat and one with a dog only       Pet[] petsInFamilies = { Pet.None, Pet.Dog | Pet.Cat, Pet.Dog };       int familiesWithoutPets = 0;       int familiesWithDog = 0;        foreach (Pet petsInFamily in petsInFamilies)       {          // Count families that have no pets.           if (petsInFamily.Equals(Pet.None))             familiesWithoutPets++;          // Of families with pets, count families that have a dog.           else if (petsInFamily.HasFlag(Pet.Dog))             familiesWithDog++;       }       Console.WriteLine("{0} of {1} families in the sample have no pets.",                          familiesWithoutPets, petsInFamilies.Length);          Console.WriteLine("{0} of {1} families in the sample have a dog.",                          familiesWithDog, petsInFamilies.Length);       } } 

The example displays the following output:

//       1 of 3 families in the sample have no pets.  //       2 of 3 families in the sample have a dog. 


回答3:

I use extension methods to write things like that :

if (letter.IsFlagSet(Letter.AB))     ... 

Here's the code :

public static class EnumExtensions {     private static void CheckIsEnum<T>(bool withFlags)     {         if (!typeof(T).IsEnum)             throw new ArgumentException(string.Format("Type '{0}' is not an enum", typeof(T).FullName));         if (withFlags && !Attribute.IsDefined(typeof(T), typeof(FlagsAttribute)))             throw new ArgumentException(string.Format("Type '{0}' doesn't have the 'Flags' attribute", typeof(T).FullName));     }      public static bool IsFlagSet<T>(this T value, T flag) where T : struct     {         CheckIsEnum<T>(true);         long lValue = Convert.ToInt64(value);         long lFlag = Convert.ToInt64(flag);         return (lValue & lFlag) != 0;     }      public static IEnumerable<T> GetFlags<T>(this T value) where T : struct     {         CheckIsEnum<T>(true);         foreach (T flag in Enum.GetValues(typeof(T)).Cast<T>())         {             if (value.IsFlagSet(flag))                 yield return flag;         }     }      public static T SetFlags<T>(this T value, T flags, bool on) where T : struct     {         CheckIsEnum<T>(true);         long lValue = Convert.ToInt64(value);         long lFlag = Convert.ToInt64(flags);         if (on)         {             lValue |= lFlag;         }         else         {             lValue &= (~lFlag);         }         return (T)Enum.ToObject(typeof(T), lValue);     }      public static T SetFlags<T>(this T value, T flags) where T : struct     {         return value.SetFlags(flags, true);     }      public static T ClearFlags<T>(this T value, T flags) where T : struct     {         return value.SetFlags(flags, false);     }      public static T CombineFlags<T>(this IEnumerable<T> flags) where T : struct     {         CheckIsEnum<T>(true);         long lValue = 0;         foreach (T flag in flags)         {             long lFlag = Convert.ToInt64(flag);             lValue |= lFlag;         }         return (T)Enum.ToObject(typeof(T), lValue);     }      public static string GetDescription<T>(this T value) where T : struct     {         CheckIsEnum<T>(false);         string name = Enum.GetName(typeof(T), value);         if (name != null)         {             FieldInfo field = typeof(T).GetField(name);             if (field != null)             {                 DescriptionAttribute attr = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;                 if (attr != null)                 {                     return attr.Description;                 }             }         }         return null;     } } 


回答4:

There is HasFlag method in .NET 4 or higher.

if(letter.HasFlag(Letters.AB)) { } 


回答5:

If you can use .NET 4 or higher than use HasFlag() method

examples

letter.HasFlag(Letters.A | Letters.B) // both A and B must be set 

same as

letter.HasFlag(Letters.AB) 


回答6:

If it really annoys you, you can write a function like that:

public bool IsSet(Letters value, Letters flag) {     return (value & flag) == flag; }  if (IsSet(letter, Letters.A)) {    // ... }  // If you want to check if BOTH Letters.A and Letters.B are set: if (IsSet(letter, Letters.A & Letters.B)) {    // ... }  // If you want an OR, I'm afraid you will have to be more verbose: if (IsSet(letter, Letters.A) || IsSet(letter, Letters.B)) {    // ... } 


回答7:

To check if for example AB is set I can do this:

if((letter & Letters.AB) == Letters.AB)

Is there a simpler way to check if any of the flags of a combined flag constant are set than the following?

This checks that both A and B are set, and ignores whether any other flags are set.

if((letter & Letters.A) == Letters.A || (letter & Letters.B) == Letters.B) 

This checks that either A or B is set, and ignores whether any other flags are set or not.

This can be simplified to:

if(letter & Letters.AB) 

Here's the C for binary operations; it should be straightforward to apply this to C#:

enum {      A = 1,      B = 2,      C = 4,      AB = A | B,      All = AB | C, };  int flags = A|C;  bool anything_and_a = flags & A;  bool only_a = (flags == A);  bool a_and_or_c_and_anything_else = flags & (A|C);  bool both_ac_and_anything_else = (flags & (A|C)) == (A|C);  bool only_a_and_c = (flags == (A|C)); 

Incidentally, the naming of the variable in the question's example is the singular 'letter', which might imply that it represents only a single letter; the example code makes it clear that its a set of possible letters and that multiple values are allowed, so consider renaming the variable 'letters'.



回答8:

How about

if ((letter & Letters.AB) > 0) 

?



回答9:

Would this work for you?

if ((letter & (Letters.A | Letters.B)) != 0) 

Regards,

Sebastiaan



回答10:

There are a lot of answers on here but I think the most idiomatic way to do this with Flags would be Letters.AB.HasFlag(letter) or (Letters.A | Letters.B).HasFlag(letter) if you didn't already have Letters.AB. letter.HasFlag(Letters.AB) only works if it has both.



回答11:

I created a simple extension method that does not need a check on Enum types:

public static bool HasAnyFlag(this Enum value, Enum flags) {     return         value != null && ((Convert.ToInt32(value) & Convert.ToInt32(flags)) != 0); } 

It also works on nullable enums. The standard HasFlag method does not, so I created an extension to cover that too.

public static bool HasFlag(this Enum value, Enum flags) {     int f = Convert.ToInt32(flags);      return         value != null && ((Convert.ToInt32(value) & f) == f); } 

A simple test:

[Flags] enum Option {     None = 0x00,     One = 0x01,     Two = 0x02,     Three = One | Two,     Four = 0x04 }  [TestMethod] public void HasAnyFlag() {     Option o1 = Option.One;     Assert.AreEqual(true, o1.HasAnyFlag(Option.Three));     Assert.AreEqual(false, o1.HasFlag(Option.Three));      o1 |= Option.Two;     Assert.AreEqual(true, o1.HasAnyFlag(Option.Three));     Assert.AreEqual(true, o1.HasFlag(Option.Three)); }  [TestMethod] public void HasAnyFlag_NullableEnum() {     Option? o1 = Option.One;     Assert.AreEqual(true, o1.HasAnyFlag(Option.Three));     Assert.AreEqual(false, o1.HasFlag(Option.Three));      o1 |= Option.Two;     Assert.AreEqual(true, o1.HasAnyFlag(Option.Three));     Assert.AreEqual(true, o1.HasFlag(Option.Three)); } 

Enjoy!



回答12:

if((int)letter != 0) { } 


回答13:

You could just check if the value is not zero.

if ((Int32)(letter & Letters.AB) != 0) { } 

But I would consider it a better solution to introduce a new enumeration value with value zero and compare agains this enumeration value (if possible because you must be able to modify the enumeration).

[Flags] enum Letters {     None = 0,     A    = 1,     B    = 2,     C    = 4,     AB   =  A | B,     All  = AB | C }  if (letter != Letters.None) { } 

UPDATE

Missread the question - fixed the first suggestion and just ignore the second suggestion.



回答14:

There are two aproaches that I can see that would work for checking for any bit being set.

Aproach A

if (letter != 0) { } 

This works as long as you don't mind checking for all bits, including non-defined ones too!

Aproach B

if ((letter & Letters.All) != 0) { } 

This only checks the defined bits, as long as Letters.All represents all of the possible bits.

For specific bits (one or more set), use Aproach B replacing Letters.All with the bits that you want to check for (see below).

if ((letter & Letters.AB) != 0) { } 


回答15:

Sorry, but i will show it in VB :)

   <Flags()> Public Enum Cnt As Integer         None = 0         One = 1         Two = 2         Three = 4         Four = 8         End Enum      Sub Test()     Dim CntValue As New Cnt     CntValue += Cnt.One     CntValue += Cnt.Three     Console.WriteLine(CntValue)     End Sub 

CntValue = 5 So the enum contains 1 + 4



回答16:

You can use this extension method on enum, for any type of enums:

public static bool IsSingle(this Enum value) {     var items = Enum.GetValues(value.GetType());     var counter = 0;     foreach (var item in items)     {         if (value.HasFlag((Enum)item))         {             counter++;         }         if (counter > 1)         {             return false;         }     }     return true; } 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!