C# 最整洁的方式';或';标记的枚举中的所有值?

C# 最整洁的方式';或';标记的枚举中的所有值?,c#,enums,C#,Enums,给定枚举: [Flags] public enum mytest { a = 1, b = 2, c = 4 } 我提出了两种在单个变量中表示所有值的方法: var OR1 = (mytest)Enum.GetNames(typeof(mytest)).Sum(a => (int)Enum.Parse(typeof(mytest), a)); var OR2 = (mytest)(typeof(mytest).GetEnumValues() as

给定
枚举

[Flags]
public enum mytest
{
    a = 1,
    b = 2,
    c = 4
}
我提出了两种在单个变量中表示所有值的方法:

    var OR1 = (mytest)Enum.GetNames(typeof(mytest)).Sum(a => (int)Enum.Parse(typeof(mytest), a));
    var OR2 = (mytest)(typeof(mytest).GetEnumValues() as mytest[]).Sum(a => (int)a);
现在,虽然他们都工作,有没有更整洁的方法?可能是我丢失了一个.NET方法


编辑:为了澄清,我需要函数是动态的-我不想通过指定每个
enum
值来计算它。

对于通用方法,使用Linq的扩展方法

var flags = Enum.GetValues(typeof(mytest))
                .Cast<int>()
                .Aggregate(0, (s, f) => s | f);
var flags=Enum.GetValues(typeof(mytest))
.Cast()
.骨料(0,(s,f)=>s | f);
或者在包装器方法中

TEnum GetAll<TEnum>() where TEnum : struct
{
    return (TEnum) (object)
            Enum.GetValues(typeof(TEnum))
                .Cast<int>()
                .Aggregate(0, (s, f) => s | f);
}
TEnum GetAll()其中TEnum:struct
{
返回(十纳姆)(对象)
枚举GetValues(typeof(TEnum))
.Cast()
.骨料(0,(s,f)=>s | f);
}

这一双重施法技巧的全部功劳归于@millimoose

如果拥有
All
成员有意义,只需直接提供:

[Flags]
public enum mytest
{
    a = 1,
    b = 2,
    c = 4,
    All = 7
}
不过,更惯用的书写方式可能是:

[Flags]
public enum MyTest
{
    A = 1,
    B = 1 << 0x01,
    C = 1 << 0x02,
    All = A | B | C
}
[标志]
公共枚举MyTest
{
A=1,
B=1用于按位或同时使用它们。即使您有表示多个设置位的枚举值,这也会起作用,而不是
Sum()

使此泛型有点棘手,因为您不能将泛型类型约束为枚举,也不能将基元类型彼此之间存在任何子类型关系。我能想到的最好方法是假设基础类型为
int
,这在大多数情况下应该足够好:

TEnum AllEnums<TEnum>() 
{
    var values = typeof(TEnum).GetEnumValues().Cast<int>();
    return (TEnum) (object) values.Aggregate((a,b) => a|b);
}
TEnum AllEnums()
{
var values=typeof(TEnum).GetEnumValues().Cast();
返回(十)(对象)值。聚合((a,b)=>a | b);
}

确保将枚举的所有位都设置为只设置所有位的最简单方法:

mytest allValues = (mytest)int.MaxValue;

这假设设置不对应于任何枚举的位没有问题,但这很可能是真的。您可以将其与任何枚举值合并,结果将是真的,这很可能是最终目标。

类似的情况如何

var all = Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>().Last() * 2 - 1;
仅当所有值都从1到最大值时,此选项才有效


1,2,4…32,64…

考虑到潜在的类型转换问题,这并不像乍一看那么简单:

static public TEnum GetAllFlags<TEnum>() where TEnum : struct, IComparable, IFormattable, IConvertible
    {
        unchecked
        {
            if (!typeof(TEnum).IsEnum)
                throw new InvalidOperationException("Can't get flags from non Enum");
            object val = null;
            switch (Type.GetTypeCode(Enum.GetUnderlyingType(typeof(TEnum))))
            {
                case TypeCode.Byte:
                case TypeCode.SByte:
                    val = Enum.GetValues(typeof(TEnum))
                                .Cast<Byte>()
                                .Aggregate(default(Byte), ( s, f) => (byte)(s | f));
                    break;
                case TypeCode.Int16:
                case TypeCode.UInt16:
                    val = Enum.GetValues(typeof(TEnum))
                                .Cast<UInt16>()
                                .Aggregate(default(UInt16), ( s, f) => (UInt16)(s | f));
                    break;
                case TypeCode.Int32:
                case TypeCode.UInt32:
                    val = Enum.GetValues(typeof(TEnum))
                                .Cast<UInt32>()
                                .Aggregate(default(UInt32), ( s, f) => (UInt32)(s | f));
                    break;
                case TypeCode.Int64:
                case TypeCode.UInt64:
                    val = Enum.GetValues(typeof(TEnum))
                                .Cast<UInt64>()
                                .Aggregate(default(UInt64), ( s, f) => (UInt64)(s | f));
                    break;
                default :
                    throw new InvalidOperationException("unhandled enum underlying type");

            }
            return (TEnum)Enum.ToObject(typeof(TEnum), val);
        }
    }
static public TEnum GetAllFlags(),其中TEnum:struct,IComparable,IFormattable,IConvertible
{
未经检查
{
if(!typeof(TEnum).IsEnum)
抛出新的InvalidOperationException(“无法从非枚举获取标志”);
对象val=null;
开关(Type.GetTypeCode(Enum.GetUnderlyingType(typeof(TEnum)))
{
大小写类型代码。字节:
案例类型代码.SByte:
val=Enum.GetValues(typeof(TEnum))
.Cast()
.Aggregate(默认值(字节),(s,f)=>(字节)(s | f));
打破
case TypeCode.Int16:
案例类型代码.UInt16:
val=Enum.GetValues(typeof(TEnum))
.Cast()
.合计(默认值(UInt16),(s,f)=>(UInt16)(s | f));
打破
case TypeCode.Int32:
案例类型代码.UInt32:
val=Enum.GetValues(typeof(TEnum))
.Cast()
.合计(默认值(UInt32),(s,f)=>(UInt32)(s | f));
打破
case TypeCode.Int64:
案例类型代码.UInt64:
val=Enum.GetValues(typeof(TEnum))
.Cast()
.合计(默认值(UInt64),(s,f)=>(UInt64)(s | f));
打破
违约:
抛出新的InvalidOperationException(“未处理的枚举基础类型”);
}
返回(TEnum)枚举对象(typeof(TEnum),val);
}
}

关于这类转换的更多信息可以在这里找到

你也可以有一个
All
enum成员,它是总和。半认真的建议:在某些情况下,你可以这样做:
int-All=~0;
不是我的否决票,但也许硬编码一个特定的enum来表示所有其他枚举的想法并不理想——如果我添加一个enum值,那么我必须这样做每次进行两次更改。另外,其他示例中的代码不起作用。在创建标志枚举时,提供等于0的None成员也是很常见的。
Sum
如果值重叠,则不起作用。例如,{1,2,4,7,8}.The
Or
是15,The
Sum
也是22,我通常使用十六进制指定标志值,因为它比十进制更容易看到级数。此外,我会编写
All=a | b | c;
而不是
All=7;
。然后添加一个新的枚举成员就像将
| newEnumMember
附加到初始值一样简单n of
All
。你确定这段代码有效吗?我想我不久前尝试了一些类似的东西,但没有。@maxp在一些简单的测试中对我很好。你能想出一个不起作用的测试用例吗?这确实有效。只是测试了它,因为我认为强制转换会引发异常,但没有。它有效。@istepaniuk显然,如果m由不同的数字字段支持,例如短或长,您需要使用相应类型的
MaxValue
方法,但这将是所有需要更改的内容。您正在创建一个“无效”enum,您不能简单地将其与==。此外…它匹配任何组合…您如何使用它来检查是否设置了所有标志?为什么需要
enum.ToObject
?我不是100%确定,但强制转换本身不起作用吗?@p.s.w.g它不起作用,因为您无法强制转换不相关的类型(
int
到不受约束的
TEnum
)是自愿的。为了使强制转换合法化,编译器必须知道它至少有可能是有意义的,并且它知道
int
不是真正的强制转换
all = max*2-1
static public TEnum GetAllFlags<TEnum>() where TEnum : struct, IComparable, IFormattable, IConvertible
    {
        unchecked
        {
            if (!typeof(TEnum).IsEnum)
                throw new InvalidOperationException("Can't get flags from non Enum");
            object val = null;
            switch (Type.GetTypeCode(Enum.GetUnderlyingType(typeof(TEnum))))
            {
                case TypeCode.Byte:
                case TypeCode.SByte:
                    val = Enum.GetValues(typeof(TEnum))
                                .Cast<Byte>()
                                .Aggregate(default(Byte), ( s, f) => (byte)(s | f));
                    break;
                case TypeCode.Int16:
                case TypeCode.UInt16:
                    val = Enum.GetValues(typeof(TEnum))
                                .Cast<UInt16>()
                                .Aggregate(default(UInt16), ( s, f) => (UInt16)(s | f));
                    break;
                case TypeCode.Int32:
                case TypeCode.UInt32:
                    val = Enum.GetValues(typeof(TEnum))
                                .Cast<UInt32>()
                                .Aggregate(default(UInt32), ( s, f) => (UInt32)(s | f));
                    break;
                case TypeCode.Int64:
                case TypeCode.UInt64:
                    val = Enum.GetValues(typeof(TEnum))
                                .Cast<UInt64>()
                                .Aggregate(default(UInt64), ( s, f) => (UInt64)(s | f));
                    break;
                default :
                    throw new InvalidOperationException("unhandled enum underlying type");

            }
            return (TEnum)Enum.ToObject(typeof(TEnum), val);
        }
    }