10

When using Enums with bit fields:

   enum  ReallyBigEnum  { FirstChoice = 0x01, AnotherOption = 0x02 }
   ReallyBigEnum  flag = ReallyBigEnum.FirstChoice | ReallyBigEnum.AnotherOption;

the code used to test the bits is:

   if( (flag & ReallyBigEnum.AnotherOption) == ReallyBigEnum.AnotherOption ) { ... }

which seems verbose and error prone because of the need to repeat the bit being tested.

It would be nice if there were some way to say:

   if( flag.IsSet( ReallyBigEnum.AnotherOption ) ) { ... }

but Enums don't support instance methods. So, I tried a template function:

   class Enums
   {
      public static bool IsSet<T>( T flag, T bit ) { return (flag & bit) == bit; }
   }

but the code to test bits then looks like this:

   if( Enums.IsSet<ReallyBigEnum>( flag, ReallyBigEnum.AnotherOption ) ) { ... }

which is a lot to write. Then I tried to shorten it:

   class Enums
   {
      public static bool IsSet( int flag, int bit ) { return (flag & bit) == bit; }
   }

but then you have to cast each value to its base type like this:

   if( Enums.IsSet( (int)flag, (int)ReallyBigEnum.AnotherOption ) ) { ... }

which is also a pain to code and loses the benefit of type checking.

The same function could be written to use 'object' parameters, but then the object type and underlying base type would have to be tested.

So, I'm stuck with the standard, redundant way at the top.

Does anyone have other ideas on a clean, simple way of testing Enum bit fields?

Much Thanks.

Chris C.
  • 915
  • 1
  • 7
  • 16

3 Answers3

6

Up to .Net 3.5 this is your only option. In .Net 4.0 there is a HasFlag method on the enum.

AxelEckenberger
  • 15,758
  • 3
  • 45
  • 67
  • The HasFlag() method is the perfect solution, of course. Thanks! Unfortunately, our development environment still uses .NET 2.0. – Chris C. Sep 16 '10 at 20:42
2
if((flag & ReallyBigEnum.AnotherOption) != 0) { ... }

UPDATED:

The above obviously work only if you're testing a single bit. If you want to test multiple bits, then something else is required, depending on whether you are checking for all bits or any bit.

Testing that any of a set of bits is set

For this case, just use a variant of the single bit version.

if((flag & (ReallyBigEnum.FirstOption | ReallyBigEnum.AnotherOption)) != 0) { ... }

Testing that all of a set of bits is set

For this case to achive clarity and reliability, I suggest creating a constant that contains all of the bits.

const int ReallyBigEnum WickedAwesomeOptions = ReallyBigEnum.FirstOption | ReallyBigEnum.AnotherOption;
...
if (flag & WickedAwesomeOptions == WickedAwesomeOptions) { ... }

Some of the redundancy is still there, but it is not fragile or confusing, and it is easy to maintain.

If the bit combination applies broadly, then you can add it to the enum itself.

[Flags]
enum ReallyBigEnum
{
    FirstOption = 1,
    AnotherOption = 2,
    WickedAwesomeOptions = FirstOption | AnotherOption,
}
....
if (flag & ReallyBigEnum.WickedAwesomeOptions == ReallyBigEnum.WickedAwesomeOptions) { ... }
Jeffrey L Whitledge
  • 53,361
  • 9
  • 64
  • 96
2
/// <summary>
/// Taken from https://stackoverflow.com/questions/9033/hidden-features-of-c/407325#407325
/// instead of doing (enum & value) == value you can now use enum.Has(value)
/// </summary>
/// <typeparam name="T">Type of enum</typeparam>
/// <param name="type">The enum value you want to test</param>
/// <param name="value">Flag Enum Value you're looking for</param>
/// <returns>True if the type has value bit set</returns>
public static bool Has<T>(this System.Enum type, T value)
{
   return (((int)(object)type & (int)(object)value) == (int)(object)value);
} 

Here is an Extension Method I took from the above link

Use:

MyFlagEnum e = MyFlagEnum.First | MyFlagEnum.Second;
if(e.Has(MyFlagEnum.First))
{
   // Do stuff
}
Community
  • 1
  • 1
PostMan
  • 6,548
  • 1
  • 40
  • 49
  • Very good solution. Documentation says that for C#, extension methods are just as efficient as original class methods. Extension methods were introduced in .NET v3.0. This code makes an assumption about the size of the Enum which can be based on unsigned and long integers as well. – Chris C. Sep 21 '10 at 05:56
  • 1
    If the cast were changed to: (long)(object)value would it work for all integer types? – Chris C. Sep 21 '10 at 06:03
  • This assumes that the Enum is backed by an integer, which is the case for standard compliant Enums, but there are more types. Byte backed Enums (as used in interop with native code) will fail here. – Bert Huijben Jan 28 '21 at 09:08