我下面有一个标志枚举.
[Flags] public enum FlagTest { None = 0x0, Flag1 = 0x1, Flag2 = 0x2, Flag3 = 0x4 }
我无法将if语句评估为true.
FlagTest testItem = FlagTest.Flag1 | FlagTest.Flag2; if (testItem == FlagTest.Flag1) { // Do something, // however This is never true. }
我怎样才能做到这一点?
在.NET 4中有一个新方法Enum.HasFlag.这允许你写:
if ( testItem.HasFlag( FlagTest.Flag1 ) ) { // Do Stuff }
IMO,它更具可读性.
.NET源表明它执行与接受的答案相同的逻辑:
public Boolean HasFlag(Enum flag) { if (!this.GetType().IsEquivalentTo(flag.GetType())) { throw new ArgumentException( Environment.GetResourceString( "Argument_EnumTypeDoesNotMatch", flag.GetType(), this.GetType())); } ulong uFlag = ToUInt64(flag.GetValue()); ulong uThis = ToUInt64(GetValue()); // test predicate return ((uThis & uFlag) == uFlag); }
if ((testItem & FlagTest.Flag1) == FlagTest.Flag1) { // Do something }
(testItem & FlagTest.Flag1)
是按位AND操作.
FlagTest.Flag1
相当于001
OP的枚举.现在让我们说testItem
有Flag1和Flag2(所以它是按位的101
):
001 &101 ---- 001 == FlagTest.Flag1
对于那些无法用可接受的解决方案(这是这个)可视化正在发生的事情的人,
if ((testItem & FlagTest.Flag1) == FlagTest.Flag1) { // Do stuff. }
testItem
(按照问题)定义为,
testItem = flag1 | flag2 = 001 | 010 = 011
然后,在if语句中,比较的左侧是,
(testItem & flag1) = (011 & 001) = 001
和完整的if语句(如果flag1
设置为testItem
,则计算结果为true ),
(testItem & flag1) == flag1 = (001) == 001 = true
@菲尔 - 德瓦尼
请注意,除了最简单的情况外,与手动编写代码相比,Enum.HasFlag会带来严重的性能损失.请考虑以下代码:
[Flags] public enum TestFlags { One = 1, Two = 2, Three = 4, Four = 8, Five = 16, Six = 32, Seven = 64, Eight = 128, Nine = 256, Ten = 512 } class Program { static void Main(string[] args) { TestFlags f = TestFlags.Five; /* or any other enum */ bool result = false; Stopwatch s = Stopwatch.StartNew(); for (int i = 0; i < 10000000; i++) { result |= f.HasFlag(TestFlags.Three); } s.Stop(); Console.WriteLine(s.ElapsedMilliseconds); // *4793 ms* s.Restart(); for (int i = 0; i < 10000000; i++) { result |= (f & TestFlags.Three) != 0; } s.Stop(); Console.WriteLine(s.ElapsedMilliseconds); // *27 ms* Console.ReadLine(); } }
超过1000万次迭代,HasFlags扩展方法需要高达4793 ms,而标准按位实现则为27 ms.
我设置了一个扩展方法来执行此操作:相关问题.
基本上:
public static bool IsSet( this Enum input, Enum matchTo ) { return ( Convert.ToUInt32( input ) & Convert.ToUInt32( matchTo ) ) != 0; }
然后你可以这样做:
FlagTests testItem = FlagTests.Flag1 | FlagTests.Flag2; if( testItem.IsSet ( FlagTests.Flag1 ) ) //Flag1 is set
顺便提一下,我用于枚举的约定对于标准是单数,对于标志是复数.这样你就可以从枚举名称中知道它是否可以包含多个值.
还有一条建议......永远不要使用值为"0"的标志进行标准二进制检查.您对此标志的检查将始终为真.
[Flags] public enum LevelOfDetail { [EnumMember(Value = "FullInfo")] FullInfo=0, [EnumMember(Value = "BusinessData")] BusinessData=1 }
如果您对FullInfo进行二进制检查输入参数 - 您会得到:
detailLevel = LevelOfDetail.BusinessData; bool bPRez = (detailLevel & LevelOfDetail.FullInfo) == LevelOfDetail.FullInfo;
bPRez将永远为真,因为ANYTHING&0总是== 0.
相反,您应该只检查输入的值是0:
bool bPRez = (detailLevel == LevelOfDetail.FullInfo);
if((testItem & FlagTest.Flag1) == FlagTest.Flag1) { ... }
对于位操作,您需要使用按位运算符.
这应该做的伎俩:
if ((testItem & FlagTest.Flag1) == FlagTest.Flag1) { // Do something, // however This is never true. }
编辑: 修复了我的检查 - 我回到了我的C/C++方式(感谢Ryan Farley指出来)
关于编辑.你无法做到这一点.我建议你将你想要的东西包装到另一个类(或扩展方法)中,以便更接近你需要的语法.
即
public class FlagTestCompare { public static bool Compare(this FlagTest myFlag, FlagTest condition) { return ((myFlag & condition) == condition); } }