161

我在下面有一个标志枚举。

[Flags]
public enum FlagTest
{
    None = 0x0,
    Flag1 = 0x1,
    Flag2 = 0x2,
    Flag3 = 0x4
}

我不能让 if 语句评估为真。

FlagTest testItem = FlagTest.Flag1 | FlagTest.Flag2;

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

我怎样才能做到这一点?

4

11 回答 11

329

在 .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); 
}
于 2009-11-20T11:04:36.407 回答
184
if ((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
     // Do something
}

(testItem & FlagTest.Flag1)是按位与运算。

FlagTest.Flag1等同于001OP 的枚举。现在假设testItem有 Flag1 和 Flag2 (所以它是按位的101):

  001
 &101
 ----
  001 == FlagTest.Flag1
于 2008-09-02T18:31:53.137 回答
79

对于那些无法想象接受的解决方案(就是这个)正在发生的事情的人,

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

testItem(根据问题)定义为,

testItem 
 = flag1 | flag2  
 = 001 | 010  
 = 011

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

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

以及完整的 if 语句(如果flag1在 中设置,则评估为 true testItem),

(testItem & flag1) == flag1
 = (001) == 001
 = true
于 2009-11-20T11:12:51.123 回答
26

@phil-devaney

请注意,除了最简单的情况外,与手动编写代码相比,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 毫秒,而标准的按位实现则需要 27 毫秒。

于 2011-08-23T16:25:40.980 回答
21

我设置了一个扩展方法来做到这一点:相关问题

基本上:

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

顺便说一句,我用于枚举的约定是标准的单数,标志的复数。这样您就可以从枚举名称中知道它是否可以包含多个值。

于 2008-09-02T18:38:17.987 回答
19

还有一条建议......永远不要对值为“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);
于 2010-08-05T07:10:44.263 回答
7
if((testItem & FlagTest.Flag1) == FlagTest.Flag1) 
{
...
}
于 2008-09-02T18:32:18.673 回答
5

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

这应该可以解决问题:

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

编辑: 修复了我的 if 检查 - 我回到了我的 C/C++ 方式(感谢 Ryan Farley 指出)

于 2008-09-02T18:30:25.463 回答
5

关于编辑。你不能让它成真。我建议你将你想要的东西包装到另一个类(或扩展方法)中,以更接近你需要的语法。

IE

public class FlagTestCompare
{
    public static bool Compare(this FlagTest myFlag, FlagTest condition)
    {
         return ((myFlag & condition) == condition);
    }
}
于 2008-09-02T18:44:47.520 回答
4

试试这个:


if ((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
    // do something
}
基本上,您的代码询问设置两个标志是否与设置一个标志相同,这显然是错误的。如果完全设置,上面的代码将只设置 Flag1 位,然后将此结果与 Flag1 进行比较。

于 2008-09-02T18:33:12.680 回答
1

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

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

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

if((testItem & (FlagTest.Flag1 | FlagTest.Flag2 ))!=FlagTest.None){
//..
}
于 2015-11-19T18:37:21.073 回答