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}.TheOr
是15,TheSum
也是22,我通常使用十六进制指定标志值,因为它比十进制更容易看到级数。此外,我会编写All=a | b | c;
而不是All=7;
。然后添加一个新的枚举成员就像将| newEnumMember
附加到初始值一样简单n ofAll
。你确定这段代码有效吗?我想我不久前尝试了一些类似的东西,但没有。@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);
}
}