说我有以下枚举:
[Flags]
enum IntFlags
{
A = 1 << 0,
B = 1 << 1,
C = 1 << 2,
AAndB = A | B
}
我想构建一个帮助方法来设置如下的标志:
private static IntFlags Set(IntFlags values, IntFlags target)
{
return target | values;
}
有没有灵活的方法来进行一些健全性检查?就像不应该设置枚举中不存在的值或者不是现有值的组合。
以下内容不应该是可能的:
IntFlags flags = 0;
flags = Set(0, flags);
flags = Set((IntFlags)int.MaxValue, flags);
flags = Set(~IntFlags.A, flags);
我想只是对现有的值进行检查,如:
private static IntFlags Set(IntFlags values, IntFlags target)
{
if (!Enum.GetValues(typeof(IntFlags)).Cast<IntFlags>().Contains(values))
throw new ArgumentException();
return target | values;
}
因为设置
等值而无法工作IntFlags flags = 0;
flags = Set(IntFlags.A | IntFlags.C, flags);
应该允许。
我认为这可行:
IntFlags Set(IntFlags values, IntFlags target)
{
int intValue;
if (int.TryParse(values.ToString(), out intValue))
{
throw new ArgumentException();
}
...
}
但这取决于我在枚举上使用Flags属性这一事实。
修改
我现在找到了Enum.IsDefined()方法。它适用于
Assert.False(Enum.IsDefined(typeof(IntFlags), (IntFlags)int.MaxValue));
Assert.False(Enum.IsDefined(typeof(IntFlags), 0));
Assert.False(Enum.IsDefined(typeof(IntFlags), ~IntFlags.A));
但是我的要求没有合并值
Assert.True(Enum.IsDefined(typeof(IntFlags), IntFlags.A | IntFlags.C)); //fails
答案 0 :(得分:6)
无法静态禁止Enum
的用户创建表示基础类型的任何值的枚举。您只能在运行时进行此类验证,如问题中所示。
如果你只是在寻找一种简单的验证方法,在运行时,如果一个特定的枚举值是一个有效的标志组合,那么这是一个足够简单的检查,假设定义的枚举值实际上增加了2的幂。对于这样的枚举,所有有效值将从0到1小于下一个最大2的幂,因此检查将变为:
if((int)values > 0 && (int)values >= 1 << 3))
throw new ArgumentException();
从技术上讲,您可以通过映射每个值来删除所有可能的未映射值,但这不是特别实用解决方案。
如果其中任何一个不是一个选项,那么你剩下的就是不使用Enum
,而是使用你自己的自定义类型,它具有你想要的确切语义:
public class IntFlags
{
public int Value { get; private set; }
private IntFlags(int value)
{
this.Value = value;
}
public static readonly IntFlags A = new IntFlags(1);
public static readonly IntFlags B = new IntFlags(2);
public static readonly IntFlags C = new IntFlags(4);
public static IntFlags operator |(IntFlags first, IntFlags second)
{
return new IntFlags(first.Value | second.Value);
}
public override bool Equals(object obj)
{
return Equals(obj as IntFlags);
}
public override bool Equals(IntFlags obj)
{
return obj != null && Value == obj.Value;
}
}
答案 1 :(得分:1)
有一个静态的IntFlag字段,你实际上添加这些值是一个很好的解决方案,当你意识到我们有一个Enum.IsDefined
,但只是FYI,你也可以采用比特屏蔽的方式。我在LINQPad中把一些东西放在一起给你一个想法:
BitVector32位于System.Collections.Specialized
中[Flags]
enum IntFlags
{
None = 0,
A = 1 << 0,
B = 1 << 1,
C = 1 << 2,
AAndB = A | B
}
void Main()
{
BitVector32 bv = new BitVector32(0);
int mask1 = BitVector32.CreateMask();
int mask2 = BitVector32.CreateMask(1 << 0);
int mask3 = BitVector32.CreateMask(1 << 1);
int mask4 = BitVector32.CreateMask(1 << 2);
bv[mask1 + mask2 + mask3 + mask4] = true;
//IntFlags flag1 = IntFlags.A | IntFlags.C;
IntFlags flag1 = (IntFlags)int.MaxValue;
if((bv.Data & (int)flag1) == (int)flag1)
"Yes".Dump();
else
"No".Dump();
}
答案 2 :(得分:1)
假设您的[Flags]
枚举具有单个位的单个值,那么您应该这样做:
public static Foo SetBits( Foo foo , Foo bar )
{
Foo value = foo | bar ;
// throw if any bits are set that are not set in the enum itself.
bool isValid = value == (AllBits&value) ;
if ( !isValid ) throw new InvalidOperationException() ;
return value ;
}
// Bitwise OR of all bits of all defined values for the enum
private static readonly Foo AllBits = (Foo) Enum
.GetValues(typeof(Foo))
.Cast<int>()
.Aggregate( (x,y) => x|y )
;