2015-01-08 78 views
0

可以按位或枚举。通常这是在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也不是很有帮助。

如何在泛型枚举上执行此操作? (是否有可能?)

+1

你不能 - 运算符是静态的,编译器无法猜测T的传入 - 它们可能不支持'|'运算符。 –

+1

您可以尝试的一件事是将值转换为“long”,执行操作并将其转换回来。 –

回答

2

没有办法对类型为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; 
} 
+0

长时间可能会更安全。 –

2

您可以创建一个静态辅助方法来为您的聚合生成所需的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; } } 
}