C# 使用泛型的逐位或逐位枚举

C# 使用泛型的逐位或逐位枚举,c#,generics,enums,C#,Generics,Enums,可以按位或枚举。通常这是在枚举上完成的 例如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); } p

可以按位或枚举。通常这是在枚举上完成的

例如
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);
}
private T CombineFlags(参数T[]标志),其中T:struct,IConvertible
{
返回标志。选择(标志=>flag)。聚合((x,y)=>x | y);
}
但是,我无法将运算符“\”应用于t和t。强制转换似乎没有帮助
struct,IConvertible
似乎是我能找到的最接近枚举的,但显然不够接近,无法使用“|”运算符。枚举也不是很有帮助


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

无法对类型为
Enum
的类型应用泛型约束,也无法应用类型重载
运算符的约束,因此您所做的任何操作都无法完全保持静态类型

您可以做的是将枚举更改为其基础整数类型,进行聚合,然后将其回滚。问题是,动态确定基础类型并对该类型执行按位or操作(同样是由于缺少对具有重载
|
运算符的类型的约束)时,您无法(轻松)解决这个问题。如果可以假定枚举的基础类型是
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;
}
private static T CombineFlags(参数T[]标志),其中T:struct,IConvertible
{
int n=flags.Select(flag=>Convert.ToInt32(flag))
.骨料((x,y)=>x | y);
返回(T)(对象)n;
}

无法对类型为
枚举的类型应用泛型约束,也无法应用类型重载
运算符的约束,因此您所做的任何操作都无法完全保持静态类型

您可以做的是将枚举更改为其基础整数类型,进行聚合,然后将其回滚。问题是,动态确定基础类型并对该类型执行按位or操作(同样是由于缺少对具有重载
|
运算符的类型的约束)时,您无法(轻松)解决这个问题。如果可以假定枚举的基础类型是
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;
}
private static T CombineFlags(参数T[]标志),其中T:struct,IConvertible
{
int n=flags.Select(flag=>Convert.ToInt32(flag))
.骨料((x,y)=>x | y);
返回(T)(对象)n;
}

您可以创建一个静态助手方法,为聚合生成所需的
函数。该函数在首次访问时生成,并缓存以供其他使用

这假定传递的类型将是枚举

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; } }
}
public T组合标志(参数T[]标志)
其中T:struct,IConvertible
{
返回flags.Select(flag=>flag.Aggregate(EnumHelper.OrFunction);
}
私有类枚举帮助器
其中T:struct,IConvertible
{
静态只读类型typeofT=typeof(T);
静态只读类型underyingtype=Enum.getunderyingtype(typeofT);
静态只读参数expression[]参数=
{ 
表达式参数(typeofT),
Expression.Parameter(typeofT)
};
静态只读Func _orFunc=Expression.Lambda(
Expression.Convert(Expression.Or(
Expression.Convert(参数[0],underyingType),
Expression.Convert(参数[1],参考类型)
),typeofT),参数);
公共静态函数或函数{get{return\u orFunc;}}
}

您可以创建一个静态助手方法,为聚合生成所需的
函数。该函数在首次访问时生成,并缓存以供其他使用

这假定传递的类型将是枚举

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; } }
}
public T组合标志(参数T[]标志)
其中T:struct,IConvertible
{
返回flags.Select(flag=>flag.Aggregate(EnumHelper.OrFunction);
}
私有类枚举帮助器
其中T:struct,IConvertible
{
静态只读类型typeofT=typeof(T);
静态只读类型underyingtype=Enum.getunderyingtype(typeofT);
静态只读参数expression[]参数=
{ 
表达式参数(typeofT),
Expression.Parameter(typeofT)
};
静态只读Func _orFunc=Expression.Lambda(
Expression.Convert(Expression.Or(
Expression.Convert(参数[0],underyingType),
Expression.Convert(参数[1],参考类型)
),typeofT),参数);
公共静态函数或函数{get{return\u orFunc;}}
}

您不能-运算符是静态的,编译器无法猜测传入的是什么-它们可能不支持
运算符。您可以尝试将值转换为
长的
,执行此操作并将其转换回。您不能-运算符是静态的,编译器无法猜测传入的t-它们可能不支持
运算符。您可以尝试的一件事是将值转换为
long
,执行此操作并将其转换回。long可能更安全。long可能更安全。