使用泛型的按位或运算枚举
Bitwise or-ing enums using generics
可以按位或枚举。通常这是在 Flags 枚举上完成的。
例如var foo = MyEnum.ABC | MyEnum.XCN
我尝试创建一种方法,使用泛型将枚举数组转换为组合枚举。
这是我尝试过的:
private T CombineFlags<T>(params T[] flags) where T : struct, IConvertible
{
return flags.Select(flag => flag).Aggregate((x, y) => x | y);
}
但是,我无法将运算符“\”应用于 T 和 T。转换似乎没有帮助。 struct, IConvertible
似乎是我能得到的最接近枚举的方法,但显然还不够接近以使用“|”操作员。 System.Enum 也不是很有帮助。
如何对通用枚举执行此操作? (可能吗?)
无法对类型为 Enum
的类型应用泛型约束,也无法应用类型重载 |
运算符的约束,因此您所做的任何事情, 必然无法保持完全静态类型化。
您可以做的是将枚举更改为其基础整数类型,进行聚合,然后将其转换回去。问题是您无法(轻松)解决动态确定基础类型并执行按位或对该类型执行的问题(同样是由于缺少对具有 |
运算符重载的类型的约束)。如果您可以假设枚举的基础类型是 int
(或任何更小的类型),那么您可以这样做,但是如果枚举的基础类型是 long
那么这段代码将休息。还有一个事实是,非枚举值可用于 T
,这些类型在传递给此方法时可能会或可能不会正常运行。
private static T CombineFlags<T>(params T[] flags) where T : struct, IConvertible
{
int n = flags.Select(flag => Convert.ToInt32(flag))
.Aggregate((x, y) => x | y);
return (T)(object)n;
}
您可以创建一个静态辅助方法来为聚合生成所需的 Or
函数。该函数在首次访问时生成并缓存以供其他使用。
这假定传递的类型将是一个枚举。
public T CombineFlags<T>(params T[] flags)
where T : struct, IConvertible
{
return flags.Select(flag => flag).Aggregate(EnumHelper<T>.OrFunction);
}
private class EnumHelper<T>
where T : struct,IConvertible
{
static readonly Type typeofT = typeof(T);
static readonly Type underlyingType = Enum.GetUnderlyingType(typeofT);
static readonly ParameterExpression[] parameters =
{
Expression.Parameter(typeofT),
Expression.Parameter(typeofT)
};
static readonly Func<T, T, T> _orFunc = Expression.Lambda<Func<T, T, T>>(
Expression.Convert(Expression.Or(
Expression.Convert(parameters[0], underlyingType),
Expression.Convert(parameters[1], underlyingType)
), typeofT), parameters).Compile();
public static Func<T, T, T> OrFunction { get { return _orFunc; } }
}
您可以使用我的库或查看源代码,了解如何对枚举值执行超快速的通用按位运算。下面是 SetFlags
方法,例如:
它可能看起来很复杂,但它的编写方式经过了超级优化,并且适用于每种底层枚举类型。 JIT 将该代码编译成运行速度几乎与直接使用按位运算符相同的代码。这是最快的通用枚举库实现:)
可以按位或枚举。通常这是在 Flags 枚举上完成的。
例如var foo = MyEnum.ABC | MyEnum.XCN
我尝试创建一种方法,使用泛型将枚举数组转换为组合枚举。
这是我尝试过的:
private T CombineFlags<T>(params T[] flags) where T : struct, IConvertible
{
return flags.Select(flag => flag).Aggregate((x, y) => x | y);
}
但是,我无法将运算符“\”应用于 T 和 T。转换似乎没有帮助。 struct, IConvertible
似乎是我能得到的最接近枚举的方法,但显然还不够接近以使用“|”操作员。 System.Enum 也不是很有帮助。
如何对通用枚举执行此操作? (可能吗?)
无法对类型为 Enum
的类型应用泛型约束,也无法应用类型重载 |
运算符的约束,因此您所做的任何事情, 必然无法保持完全静态类型化。
您可以做的是将枚举更改为其基础整数类型,进行聚合,然后将其转换回去。问题是您无法(轻松)解决动态确定基础类型并执行按位或对该类型执行的问题(同样是由于缺少对具有 |
运算符重载的类型的约束)。如果您可以假设枚举的基础类型是 int
(或任何更小的类型),那么您可以这样做,但是如果枚举的基础类型是 long
那么这段代码将休息。还有一个事实是,非枚举值可用于 T
,这些类型在传递给此方法时可能会或可能不会正常运行。
private static T CombineFlags<T>(params T[] flags) where T : struct, IConvertible
{
int n = flags.Select(flag => Convert.ToInt32(flag))
.Aggregate((x, y) => x | y);
return (T)(object)n;
}
您可以创建一个静态辅助方法来为聚合生成所需的 Or
函数。该函数在首次访问时生成并缓存以供其他使用。
这假定传递的类型将是一个枚举。
public T CombineFlags<T>(params T[] flags)
where T : struct, IConvertible
{
return flags.Select(flag => flag).Aggregate(EnumHelper<T>.OrFunction);
}
private class EnumHelper<T>
where T : struct,IConvertible
{
static readonly Type typeofT = typeof(T);
static readonly Type underlyingType = Enum.GetUnderlyingType(typeofT);
static readonly ParameterExpression[] parameters =
{
Expression.Parameter(typeofT),
Expression.Parameter(typeofT)
};
static readonly Func<T, T, T> _orFunc = Expression.Lambda<Func<T, T, T>>(
Expression.Convert(Expression.Or(
Expression.Convert(parameters[0], underlyingType),
Expression.Convert(parameters[1], underlyingType)
), typeofT), parameters).Compile();
public static Func<T, T, T> OrFunction { get { return _orFunc; } }
}
您可以使用我的库或查看源代码,了解如何对枚举值执行超快速的通用按位运算。下面是 SetFlags
方法,例如:
它可能看起来很复杂,但它的编写方式经过了超级优化,并且适用于每种底层枚举类型。 JIT 将该代码编译成运行速度几乎与直接使用按位运算符相同的代码。这是最快的通用枚举库实现:)