如何比较C#中的标志?

我有一个标志枚举下面。

[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 } 

这是更可读,海事组织。

.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)是一个按位与操作。

FlagTest.Flag1与OP的enum等效于001 。 现在让我们说, testItemtestItem和Flag2(所以它是按位101 ):

  001 &101 ---- 001 == FlagTest.Flag1 

对于那些在接受的解决scheme(这是这个问题)中想象中发生什么事情的人来说,

 if ((testItem & FlagTest.Flag1) == FlagTest.Flag1) { // Do stuff. } 

testItem (根据问题)被定义为,

 testItem = flag1 | flag2 = 001 | 010 = 011 

然后,在if语句中,比较的左边是,

 (testItem & flag1) = (011 & 001) = 001 

而完整的if语句(如果在flag1中设置了testItem ,则flag1 true)

 (testItem & flag1) == flag1 = (001) == 001 = true 

我build立了一个扩展方法来做到这一点: 相关的问题 。

基本上:

 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 

顺便说一下,我用于枚举的约定是标准的单数,标志的复数。 通过这种方式,您可以从枚举名称中知道它是否可以保存多个值。

@菲尔 – 德瓦尼

请注意,除了最简单的情况之外,与手动编写代码相比, 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(); } } 

超过一千万次迭代,HasFlags扩展方法需要高达4793毫秒,而标准按位实现需要27毫秒。

还有一个build议…不要用标志值为“0”的标准二进制检查。 你对这个标志的检查将永远是真实的。

 [Flags] public enum LevelOfDetail { [EnumMember(Value = "FullInfo")] FullInfo=0, [EnumMember(Value = "BusinessData")] BusinessData=1 } 

如果您使用FullInfo对input参数进行二进制检查,则会得到:

 detailLevel = LevelOfDetail.BusinessData; bool bPRez = (detailLevel & LevelOfDetail.FullInfo) == LevelOfDetail.FullInfo; 

bPRez永远是真的任何东西&0总是== 0。


相反,你应该检查input的值是否为0:

 bool bPRez = (detailLevel == LevelOfDetail.FullInfo); 
 if((testItem & FlagTest.Flag1) == FlagTest.Flag1) { ... } 

尝试这个:

 if ((testItem & FlagTest.Flag1) == FlagTest.Flag1) { // do something } 

基本上,你的代码是问两个标志设置是否与一个标志设置相同,这显然是错误的。 如果上面的代码完全置位,则只会留下Flag1位,然后将此结果与Flag1进行比较。

对于位操作,您需要使用按位运算符。

这应该做的伎俩:

 if ((testItem & FlagTest.Flag1) == FlagTest.Flag1) { // Do something, // however This is never true. } 

编辑:修复我如果检查 – 我滑回到我的C / C ++的方式(感谢瑞安法利指出)

关于编辑。 你不能这样做。 我build议你把你想要的东西换成另一个类(或扩展方法),以便更接近你所需要的语法。

 public class FlagTestCompare { public static bool Compare(this FlagTest myFlag, FlagTest condition) { return ((myFlag & condition) == condition); } } 

即使没有[Flags],你也可以使用这样的东西

 if((testItem & (FlagTest.Flag1 | FlagTest.Flag2 ))!=0){ //.. } 

或者如果你有一个零值枚举

 if((testItem & (FlagTest.Flag1 | FlagTest.Flag2 ))!=FlagTest.None){ //.. }