我下面有一个标志枚举。
1 2 3 4 5 6 7 8
| [Flags]
public enum FlagTest
{
None = 0x0,
Flag1 = 0x1,
Flag2 = 0x2,
Flag3 = 0x4
} |
我无法使if语句评估为true。
1 2 3 4 5 6 7
| FlagTest testItem = FlagTest.Flag1 | FlagTest.Flag2;
if (testItem == FlagTest.Flag1)
{
// Do something,
// however This is never true.
} |
我如何才能做到这一点?
在.NET 4中,有一个新方法Enum.HasFlag。这使您可以编写:
1 2 3 4
| if ( testItem.HasFlag( FlagTest.Flag1 ) )
{
// Do Stuff
} |
IMO更具可读性。
.NET源表明这与接受的答案具有相同的逻辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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);
} |
1 2 3 4
| if ((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
// Do something
} |
(testItem & FlagTest.Flag1)是按位与运算。
FlagTest.Flag1等同于带有OP枚举的001。现在,假设testItem具有Flag1和Flag2(因此按位101):
1 2 3 4
| 001
&101
----
001 == FlagTest.Flag1 |
对于那些无法想象被接受的解决方案正在发生什么的人
(这是),
1 2 3 4
| if ((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
// Do stuff.
} |
testItem(根据问题)定义为
1 2 3 4
| testItem
= flag1 | flag2
= 001 | 010
= 011 |
然后,在if语句中,比较的左侧是
1 2 3
| (testItem & flag1)
= (011 & 001)
= 001 |
以及完整的if语句(如果在testItem中设置了flag1,则结果为true),
1 2 3
| (testItem & flag1) == flag1
= (001) == 001
= true |
@ phil-devaney
请注意,除了最简单的情况外,与手动编写代码相比,Enum.HasFlag会带来严重的性能损失。考虑以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| [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扩展方法经过1000万次迭代,耗时长达4793毫秒,而标准按位实现的耗时为27毫秒。
我设置了一个扩展方法来做到这一点:相关问题。
基本上:
1 2 3 4
| public static bool IsSet( this Enum input, Enum matchTo )
{
return ( Convert.ToUInt32( input ) & Convert.ToUInt32( matchTo ) ) != 0;
} |
然后,您可以执行以下操作:
1 2 3 4
| FlagTests testItem = FlagTests.Flag1 | FlagTests.Flag2;
if( testItem.IsSet ( FlagTests.Flag1 ) )
//Flag1 is set |
顺便说一下,我用于枚举的约定对于标准是单数,对于标志是复数。这样,您就可以从枚举名称中知道它是否可以容纳多个值。
另一条建议...永远不要对值为" 0"的标志进行标准的二进制检查。您对此标志的检查将始终为true。
1 2 3 4 5 6 7 8
| [Flags]
public enum LevelOfDetail
{
[EnumMember(Value ="FullInfo")]
FullInfo=0,
[EnumMember(Value ="BusinessData")]
BusinessData=1
} |
如果对FullInfo二进制检查输入参数-您将得到:
1 2
| detailLevel = LevelOfDetail.BusinessData;
bool bPRez = (detailLevel & LevelOfDetail.FullInfo) == LevelOfDetail.FullInfo; |
bPRez将始终为true,并且ANYTHING&0 always == 0。
相反,您应该只检查输入值是否为0:
1
| bool bPRez = (detailLevel == LevelOfDetail.FullInfo); |
1 2 3 4
| if((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
...
} |
关于编辑。您无法实现。我建议您将所需的内容包装到另一个类(或扩展方法)中,以更接近所需的语法。
即
1 2 3 4 5 6 7
| public class FlagTestCompare
{
public static bool Compare(this FlagTest myFlag, FlagTest condition)
{
return ((myFlag & condition) == condition);
}
} |
对于位运算,您需要使用按位运算符。
这应该可以解决问题:
1 2 3 4 5
| if ((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
// Do something,
// however This is never true.
} |
编辑:修复了我的if支票-我重新使用了C / C ++的方式(感谢Ryan Farley指出了这一点)
尝试这个:
1 2 3 4
| if ((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
// do something
} |
基本上,您的代码在询问是否设置两个标志都与设置一个标志相同,这显然是错误的。如果上面的代码被置位,则上面的代码将仅保留Flag1的位置,然后将该结果与Flag1进行比较。
即使没有[Flags],您也可以使用这样的内容
1 2 3
| if((testItem & (FlagTest.Flag1 | FlagTest.Flag2 ))!=0){
//..
} |
或者您有一个零值枚举
1 2 3
| if((testItem & (FlagTest.Flag1 | FlagTest.Flag2 ))!=FlagTest.None){
//..
} |