1
votes

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?

2

2 Answers

3
votes

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;
0
votes

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.)