位掩码枚举的通用单标志枚举

时间:2019-12-09 11:04:10

标签: c# enums ienumerable bitmask

我正在使用带有几个位标记枚举的代码库工作,这些枚举看起来像这样

public enum BitMask
{
    None = 0,

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

    OptionAandB = OptionA | OptionB,

    All = ~0
} 

我可以使用

遍历所有枚举值
public IEnumerable<T> EnumValues<T>()
{
    return Enum.GetValues(typeof(T)).Cast<T>();
}

我正在寻找一种仅迭代单个标志值的通用方法,在这种情况下,是OptionAOptionB不是 NoneOptionAandBAll。我可以强制转换为long并检测单个标志,因为它们是2的幂,因此在此处进行快速搜索可以发现这一点

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

非能量版本可以正常工作

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

但是通用版本无法编译,因为它不喜欢强制转换为long

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

有什么办法解决吗?

2 个答案:

答案 0 :(得分:3)

由于Convert.ToInt64的许多重载,您可以使用它:

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

正如评论中所建议的,这里是为了优化:

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;

答案 1 :(得分:0)

这里是可供比较的替代版本:

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;
    }
}

(我的时间表明,这比使用Convert.ToInt64(e)快1.5倍,但是对于如此快速的操作,这可能无关紧要。无论如何它仍然可以投射。)

相关问题