如何比较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
。 现在让我们说, testItem
有testItem
和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){ //.. }