1

I'm working on a codebase with several bit flags enums which look something like this

public enum BitMask
{
    None = 0,

    OptionA = 1 << 0,
    OptionB = 1 << 1,

    OptionAandB = OptionA | OptionB,

    All = ~0
} 

I can iterate over all enum values using this

public IEnumerable<T> EnumValues<T>()
{
    return Enum.GetValues(typeof(T)).Cast<T>();
}

I'm looking for a generic way to iterate over single flag values only, in this case, OptionA and OptionB. Not None, OptionAandB, All. I can cast to long and detect single flags as they are a power of two, a quick search on here suggests this

public bool IsPowerOfTwo(long val)
{
    return (val != 0) && ((val & (val-1)) == 0) ;
}

Nongeneric version works fine

public IEnumerable<BitMask> SingleFlagBitMaskValues()
{
    return Enum.GetValues(typeof(BitMask))
               .Cast<BitMask>()
               .Where(e => IsPowerOfTwo((long)e));
}

But the generic version doesn't compile because it doesn't like the cast to long

public IEnumerable<TEnum> SingleFlagEnumValues<TEnum>()
{
    return Enum.GetValues(typeof(TEnum))
        .Cast<TEnum>()
        .Where(e => IsPowerOfTwo((long)e));
}

Any way around this?

Ruben Martirosyan
  • 870
  • 1
  • 12
  • 23
jbat100
  • 16,757
  • 4
  • 45
  • 70

2 Answers2

3

You can use Convert.ToInt64 due to its many overloads:

public IEnumerable<TEnum> SingleFlagEnumValues<TEnum>()
{
    return Enum.GetValues(typeof(TEnum))
        .Cast<TEnum>()
        .Where(e => IsPowerOfTwo(Convert.ToInt64(e)));
}

As suggested in comments, here's for optimisation:

static class SingleFlagCache<TEnum>
{
    internal static TEnum[] values = Enum.GetValues(typeof(TEnum))
        .Cast<TEnum>()
        .Where(e => IsPowerOfTwo(Convert.ToInt64(e))).ToArray();
}
public static IEnumerable<TEnum> SingleFlagEnumValues<TEnum>()
    => SingleFlagCache<TEnum>.values;
jbat100
  • 16,757
  • 4
  • 45
  • 70
petelids
  • 12,305
  • 3
  • 47
  • 57
  • 2
    this should indeed work, and it is possibly the *least ugly* of the available workarounds for the limitations of generics, but: it is *really* very inefficient - lots of boxing and type testing; IMO, this would strongly benefit from a static hoist - possibly something like https://gist.github.com/mgravell/4e684204899b4ef3a33bb1c495b4fbe2 – Marc Gravell Dec 09 '19 at 11:35
  • Thanks @MarcGravell, I agree. Feel free to edit that into this answer if you'd like to :). – petelids Dec 09 '19 at 11:37
  • 1
    Added suggested optimisation and accepted answer, thanks – jbat100 Dec 10 '19 at 10:06
0

Here's an alternative version for comparison:

public static IEnumerable<TEnum> SingleFlagEnumValues<TEnum>() where TEnum: struct, Enum
{
    var type = typeof(TEnum);

    foreach (int value in Enum.GetValues(type))
    {
        if (IsPowerOfTwo(value))
            yield return (TEnum)(object)value;
    }
}

(My timings indicate that this is about 1.5 times faster than using Convert.ToInt64(e), but for such a fast operation this likely doesn't matter. It still does casting anyway.)

Matthew Watson
  • 104,400
  • 10
  • 158
  • 276