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

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 方法,例如:

https://github.com/Singulink/Singulink.Enums/blob/d74f425f71b36189f0f1dbeb38b38d51ef7efa1d/Source/Singulink.Enums/EnumExtensions.cs#L152

它可能看起来很复杂,但它的编写方式经过了超级优化,并且适用于每种底层枚举类型。 JIT 将该代码编译成运行速度几乎与直接使用按位运算符相同的代码。这是最快的通用枚举库实现:)