19

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?

George Mauer
  • 117,483
  • 131
  • 382
  • 612

7 Answers7

36
bool IsCDF(MyEnum enumValue) 
{
  return new[]{MyEnum.C, MyEnum.D, MyEnum.F}.Contains(enumValue);
}
Magnus
  • 45,362
  • 8
  • 80
  • 118
28

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
}
Jason Williams
  • 56,972
  • 11
  • 108
  • 137
  • Yeah, I pad and paper-ed something similar. I wanted to use this with the BCL's ConnectionState which Sadly does have ConnectionState.Closed = 0 - I could raise everything to a power of two but we're leaving the range of 'being slick' then. – George Mauer Sep 19 '11 at 22:14
  • Although, if the list I'm checking against does not contain a 0 then it should always evaluate to false anyways... – George Mauer Sep 19 '11 at 22:19
  • Yep, I am 2 years late - but I would advise against this, because it is dangerous in the long run. Not very readable, not very clean. – FrankB Jun 06 '13 at 12:27
  • Yes, these says you can use Enum.HasFlag() which is much more readable. – Jason Williams Jun 06 '13 at 15:47
5

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:

Sebastian Hofmann
  • 1,440
  • 6
  • 15
  • 21
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
2

Here is an extension I created that allows you to see if your given Enum value is in a variable list of possible choices of Enum values.

using System.Linq;
public static class ExtensionMethods
{
    public static bool IsAny<T>(this T value, params T[] choices)
        where T : Enum
    {
        return choices.Contains(value);
    }
}

Usage

bool IsCDF(MyEnum enumValue) 
{
    return enumValue.IsAny(MyEnum.C, MyEnum.D, MyEnum.F);
}
Asher G.
  • 4,903
  • 5
  • 27
  • 30
2

May be you are thinking of FlagsAttribute. Look at here and here for some examples.

You could use Enum.HasFlag Method

Community
  • 1
  • 1
CharithJ
  • 46,289
  • 20
  • 116
  • 131
  • 2
    Enum.HasFlag() tests for *all* of the flags, not *any* of the flags. – Suncat2000 May 02 '14 at 19:35
  • Welcome to Stack Exchange. This is a [questions and answers site](https://stackoverflow.com/about), not a link collection. Please include relevant content in your answer, not just a link to where the content may be. The link is nice to have in addition for reference or for further information. For more tips, see [How to answer](https://stackoverflow.com/questions/how-to-answer). – Our Man in Bananas Aug 16 '18 at 15:21
1

Maybe this extension class is usefull for you:

public static class Flags
{
    /// <summary>
    /// Checks if the type has any flag of value.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="type"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static bool HasAny<T>(this System.Enum type, T value)
    {
        try
        {
            return (((int) (object) type & (int) (object) value) != 0);
        }
        catch
        {
            return false;
        }
    }

    /// <summary>
    /// Checks if the value contains the provided type.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="type"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static bool Has<T>(this System.Enum type, T value)
    {
        try
        {
            return (((int)(object)type & (int)(object)value) == (int)(object)value);
        }
        catch
        {
            return false;
        }
    }

    /// <summary>
    /// Checks if the value is only the provided type.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="type"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static bool Is<T>(this System.Enum type, T value)
    {
        try
        {
            return (int)(object)type == (int)(object)value;
        }
        catch
        {
            return false;
        }
    }

    /// <summary>
    /// Appends a value.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="type"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static T Add<T>(this System.Enum type, T value)
    {
        try
        {
            return (T)(object)(((int)(object)type | (int)(object)value));
        }
        catch (Exception ex)
        {
            throw new ArgumentException(
                string.Format(
                    "Could not append value from enumerated type '{0}'.",
                    typeof(T).Name
                    ), ex);
        }
    }

    /// <summary>
    /// Appends a value.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="type"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static void AddTo<T>(this System.Enum type, ref T value)
    {
        try
        {
            value = (T)(object)(((int)(object)type | (int)(object)value));
        }
        catch (Exception ex)
        {
            throw new ArgumentException(
                string.Format(
                    "Could not append value from enumerated type '{0}'.",
                    typeof(T).Name
                    ), ex);
        }
    }

    /// <summary>
    /// Removes the value.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="type"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static T Remove<T>(this System.Enum type, T value)
    {
        try
        {
            return (T)(object)(((int)(object)type & ~(int)(object)value));
        }
        catch (Exception ex)
        {
            throw new ArgumentException(
                string.Format(
                    "Could not remove value from enumerated type '{0}'.",
                    typeof(T).Name
                    ), ex);
        }
    }

    /// <summary>
    /// Removes the value.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="type"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static void RemoveFrom<T>(this System.Enum type, ref T value)
    {
        try
        {
            value = (T)(object)(((int)(object)type & ~(int)(object)value));
        }
        catch (Exception ex)
        {
            throw new ArgumentException(
                string.Format(
                    "Could not remove value from enumerated type '{0}'.",
                    typeof(T).Name
                    ), ex);
        }
    }
}
Felix C
  • 1,755
  • 5
  • 26
  • 40
0
return (enumValue & MyEnum.C == MyEnum.C) 
       || (enumValue & MyEnum.D == MyEnum.D) 
       || (enumValue & MyEnum.F == MyEnum.F);
Mark H
  • 13,797
  • 4
  • 31
  • 45