使用泛型的按位或枚举枚举

时间:2015-01-08 16:53:31

标签: c# generics enums

可以按位或枚举。通常,这是在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);
}

但是,我无法应用运算符&#39; \&#39; T和T. Casting似乎没有帮助。 struct, IConvertible似乎是最接近枚举的,但显然不够接近使用&#39; |&#39;运营商。 System.Enum也不是很有帮助。

如何在通用枚举上执行此操作? (有可能吗?)

2 个答案:

答案 0 :(得分: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;
}

答案 1 :(得分: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; } }
}