问题
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?
回答1:
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;
回答2:
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.)
来源:https://stackoverflow.com/questions/59247510/generic-single-flag-enumeration-for-bitmask-enums