当前位置:  开发笔记 > 编程语言 > 正文

如何比较C#中的标志?

如何解决《如何比较C#中的标志?》经验,为你挑选了9个好方法。

我下面有一个标志枚举.

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

我怎样才能做到这一点?



1> Phil Devaney..:

在.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); 
}


啊,最后一些开箱即用的东西.这很棒,我一直在等待这个相对简单的功能很长一段时间.很高兴他们决定放进去.
但请注意,下面的答案显示了此方法的性能问题 - 这可能是某些人的问题.幸福不适合我.

2> Scott Nichol..:
if ((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
     // Do something
}

(testItem & FlagTest.Flag1) 是按位AND操作.

FlagTest.Flag1相当于001OP的枚举.现在让我们说testItem有Flag1和Flag2(所以它是按位的101):

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


@ IanR.O'Brien Flag1 | 标志2转换为001或010,它与011相同,现在如果你做相等的011 == Flag1或转换为011 == 001,则总是返回false.现在,如果你使用Flag1进行按位AND,则转换为011 AND 001,返回001现在执行相等性返回true,因为001 == 001
这里的逻辑究竟是什么?为什么谓词必须这样写?

3> Sekhat..:

对于那些无法用可接受的解决方案(这是这个)可视化正在发生的事情的人,

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



4> Chuck Dee..:

@菲尔 - 德瓦尼

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


确实.如果你看看HasFlag的实现,你会发现它在两个操作数上都有一个"GetType()",这很慢.然后它执行"Enum.ToUInt64(value.GetValue());" 在进行按位检查之前在两个操作数上.

5> Keith..:

我设置了一个扩展方法来执行此操作:相关问题.

基本上:

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

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


@Keith:另外,有一个显着的区别:((FlagTest)0x1).HasFlag(0x0)将返回true,这可能是也可能不是想要的行为

6> Leonid..:

还有一条建议......永远不要使用值为"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);



7> Damian..:
if((testItem & FlagTest.Flag1) == FlagTest.Flag1) 
{
...
}



8> 17 of 26..:

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

这应该做的伎俩:

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

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



9> Martin Clark..:

关于编辑.你无法做到这一点.我建议你将你想要的东西包装到另一个类(或扩展方法)中,以便更接近你需要的语法.

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

推荐阅读
和谐啄木鸟
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有