Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C#: Best way to check against a set of Enum Values?

Tags:

c#

enums

math

suppose you have enum MyEnum {A = 0, B = 1, C = 2, D = 4, E = 8, F = 16};

At some point you have a function that will check an instance of MyEnum and return true if it is C,D, or F

bool IsCDF(MyEnum enumValue) 
{
  return //something slick
}

I remember that there was some really slick way to do bit shifting and preform this operation that read way better than a bunch of ternary if statements but for the life of me I can't remember what it is.

Anyone know?

like image 223
George Mauer Avatar asked Sep 19 '11 21:09

George Mauer


3 Answers

bool IsCDF(MyEnum enumValue) 
{
  return new[]{MyEnum.C, MyEnum.D, MyEnum.F}.Contains(enumValue);
}
like image 198
Magnus Avatar answered Oct 23 '22 02:10

Magnus


If you make it a [Flags] enum, you can assign a different bit value (1, 2, 4, 8, 16...) to each enumerated value. Then you can use a bitwise operation to determine if a value is one of a set of possible values.

So, to see if it is C, D, or F:

bool IsCDF(MyEnum enumValue)
{
    return ((enumValue & (MyEnum.C | MyEnum.D | MyEnum.F)) != 0);
}

or using HasFlag() (less efficient but more readable):

bool IsCDF(MyEnum enumValue)
{
    return enumValue.HasFlag(MyEnum.C | MyEnum.D | MyEnum.F);
}

Note that this will not work for a value of 0 (in your example, 'A'), and you must be careful that all enum values resolve to unique bit values (i.e. non-zero powers of two).

The advantages of this approach are:

  • it will typically take a single CPU instruction/cycle to execute, whereas doing three separate "if" checks will take 3 or more instructions (depending on your target platform).
  • You can pass the set of values that you want to test with as an enum value (a single integer) instead of needing to use lists of enum values.
  • You can do lots of other useful things with bitwise operations, which would be clunky and slow with ordinary numerical/comparative approaches.

Handy hint: When defining [Flags] enums, use left-shift (<<) to make the bit values clearer (and much harder to get wrong) especially for higher-order bits:

[Flags]
enum MyEnum
{
    A = 1 << 0,     // Equivalent to 1
    B = 1 << 1,     // Equivalent to 2
    C = 1 << 2,     // Equivalent to 4
    D = 1 << 3,     // Equivalent to 8
    …
    Big = 1 << 26,  // Equivalent to 67108864
}
like image 28
Jason Williams Avatar answered Oct 23 '22 03:10

Jason Williams


I'd possibly use Unconstrained Melody as a way of keeping things tidy:

if (value.HasAny(MyEnum.C | MyEnum.D | MyEnum.E))
{
    ...
}

I'd probably extract the "C, D or E" bit into a named constant - possibly in the enum itself, if it had meaning:

like image 5
Jon Skeet Avatar answered Oct 23 '22 02:10

Jon Skeet