位掩码枚举的通用单标志枚举
Generic single flag enumeration for bitmask enums
我正在开发一个包含多个位标志枚举的代码库,看起来像这样
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>();
}
我正在寻找一种仅迭代单个标志值的通用方法,在本例中为 OptionA
和 OptionB
。 不是 None
、OptionAandB
、All
。我可以投射到 long
并检测单个标志,因为它们是两个的幂,在这里快速搜索表明这个
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));
}
有什么解决办法吗?
您可以使用 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;
这里有一个可供比较的替代版本:
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 倍,但对于如此快的操作,这可能并不重要。无论如何它仍然会进行转换。)
我正在开发一个包含多个位标志枚举的代码库,看起来像这样
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>();
}
我正在寻找一种仅迭代单个标志值的通用方法,在本例中为 OptionA
和 OptionB
。 不是 None
、OptionAandB
、All
。我可以投射到 long
并检测单个标志,因为它们是两个的幂,在这里快速搜索表明这个
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));
}
有什么解决办法吗?
您可以使用 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;
这里有一个可供比较的替代版本:
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 倍,但对于如此快的操作,这可能并不重要。无论如何它仍然会进行转换。)