如何检查是否设置了标志组合的标志?

时间:2009-08-27 09:44:00

标签: c# enums flags

让我说我有这个枚举:

[Flags]
enum Letters
{
     A = 1,
     B = 2,
     C = 4,
     AB = A | B,
     All = A | B | C,
}

要检查是否设置了AB,我可以这样做:

if((letter & Letters.AB) == Letters.AB)

是否有更简单的方法来检查组合标志常量的任何标志是否设置为以下值?

if((letter & Letters.A) == Letters.A || (letter & Letters.B) == Letters.B)

您可以举例说明&吗?

对于像这样的二进制文件来说不太稳定......

17 个答案:

答案 0 :(得分:148)

在.NET 4中,您可以使用Enum.HasFlag method

using System;

[Flags] public enum Pet {
   None = 0,
   Dog = 1,
   Cat = 2,
   Bird = 4,
   Rabbit = 8,
   Other = 16
}

public class Example
{
   public static void Main()
   {
      // Define three families: one without pets, one with dog + cat and one with a dog only
      Pet[] petsInFamilies = { Pet.None, Pet.Dog | Pet.Cat, Pet.Dog };
      int familiesWithoutPets = 0;
      int familiesWithDog = 0;

      foreach (Pet petsInFamily in petsInFamilies)
      {
         // Count families that have no pets. 
         if (petsInFamily.Equals(Pet.None))
            familiesWithoutPets++;
         // Of families with pets, count families that have a dog. 
         else if (petsInFamily.HasFlag(Pet.Dog))
            familiesWithDog++;
      }
      Console.WriteLine("{0} of {1} families in the sample have no pets.", 
                        familiesWithoutPets, petsInFamilies.Length);   
      Console.WriteLine("{0} of {1} families in the sample have a dog.", 
                        familiesWithDog, petsInFamilies.Length);   
   }
}

该示例显示以下输出:

//       1 of 3 families in the sample have no pets. 
//       2 of 3 families in the sample have a dog.

答案 1 :(得分:113)

如果您想知道AB中是否包含任何字母,您必须使用 AND &运营商。类似的东西:

if ((letter & Letters.AB) != 0)
{
    // Some flag (A,B or both) is enabled
}
else
{
    // None of them are enabled
}

答案 2 :(得分:57)

我使用扩展方法编写类似的东西:

if (letter.IsFlagSet(Letter.AB))
    ...

以下是代码:

public static class EnumExtensions
{
    private static void CheckIsEnum<T>(bool withFlags)
    {
        if (!typeof(T).IsEnum)
            throw new ArgumentException(string.Format("Type '{0}' is not an enum", typeof(T).FullName));
        if (withFlags && !Attribute.IsDefined(typeof(T), typeof(FlagsAttribute)))
            throw new ArgumentException(string.Format("Type '{0}' doesn't have the 'Flags' attribute", typeof(T).FullName));
    }

    public static bool IsFlagSet<T>(this T value, T flag) where T : struct
    {
        CheckIsEnum<T>(true);
        long lValue = Convert.ToInt64(value);
        long lFlag = Convert.ToInt64(flag);
        return (lValue & lFlag) != 0;
    }

    public static IEnumerable<T> GetFlags<T>(this T value) where T : struct
    {
        CheckIsEnum<T>(true);
        foreach (T flag in Enum.GetValues(typeof(T)).Cast<T>())
        {
            if (value.IsFlagSet(flag))
                yield return flag;
        }
    }

    public static T SetFlags<T>(this T value, T flags, bool on) where T : struct
    {
        CheckIsEnum<T>(true);
        long lValue = Convert.ToInt64(value);
        long lFlag = Convert.ToInt64(flags);
        if (on)
        {
            lValue |= lFlag;
        }
        else
        {
            lValue &= (~lFlag);
        }
        return (T)Enum.ToObject(typeof(T), lValue);
    }

    public static T SetFlags<T>(this T value, T flags) where T : struct
    {
        return value.SetFlags(flags, true);
    }

    public static T ClearFlags<T>(this T value, T flags) where T : struct
    {
        return value.SetFlags(flags, false);
    }

    public static T CombineFlags<T>(this IEnumerable<T> flags) where T : struct
    {
        CheckIsEnum<T>(true);
        long lValue = 0;
        foreach (T flag in flags)
        {
            long lFlag = Convert.ToInt64(flag);
            lValue |= lFlag;
        }
        return (T)Enum.ToObject(typeof(T), lValue);
    }

    public static string GetDescription<T>(this T value) where T : struct
    {
        CheckIsEnum<T>(false);
        string name = Enum.GetName(typeof(T), value);
        if (name != null)
        {
            FieldInfo field = typeof(T).GetField(name);
            if (field != null)
            {
                DescriptionAttribute attr = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;
                if (attr != null)
                {
                    return attr.Description;
                }
            }
        }
        return null;
    }
}

答案 3 :(得分:27)

.NET 4或更高版本中有HasFlag方法。

if(letter.HasFlag(Letters.AB))
{
}

答案 4 :(得分:18)

如果可以使用.NET 4或更高版本而不是使用HasFlag()方法

实施例

letter.HasFlag(Letters.A | Letters.B) // both A and B must be set

相同
letter.HasFlag(Letters.AB)

答案 5 :(得分:11)

如果真的让你烦恼,你可以写一个这样的函数:

public bool IsSet(Letters value, Letters flag)
{
    return (value & flag) == flag;
}

if (IsSet(letter, Letters.A))
{
   // ...
}

// If you want to check if BOTH Letters.A and Letters.B are set:
if (IsSet(letter, Letters.A & Letters.B))
{
   // ...
}

// If you want an OR, I'm afraid you will have to be more verbose:
if (IsSet(letter, Letters.A) || IsSet(letter, Letters.B))
{
   // ...
}

答案 6 :(得分:7)

  

要检查是否设置了AB,我可以这样做:

     

if((letter&amp; Letters.AB)== Letters.AB)

     

是否有更简单的方法来检查组合标志常量的任何标志是否设置为以下值?

这将检查 两者 A和B是否已设置,并忽略是否设置了其他任何标志。

if((letter & Letters.A) == Letters.A || (letter & Letters.B) == Letters.B)

检查 A或B是否已设置,并忽略是否设置了其他任何标志。

这可以简化为:

if(letter & Letters.AB)

这是二进制操作的C;它应该直接应用于C#:

enum {
     A = 1,
     B = 2,
     C = 4,
     AB = A | B,
     All = AB | C,
};

int flags = A|C;

bool anything_and_a = flags & A;

bool only_a = (flags == A);

bool a_and_or_c_and_anything_else = flags & (A|C);

bool both_ac_and_anything_else = (flags & (A|C)) == (A|C);

bool only_a_and_c = (flags == (A|C));

顺便提一下,问题示例中变量的命名是单数“字母”,这可能意味着它只代表一个字母;示例代码清楚地表明它是一组可能的字母并且允许多个值,因此请考虑重命名变量'letters'。

答案 7 :(得分:3)

怎么样

if ((letter & Letters.AB) > 0)

答案 8 :(得分:3)

我创建了一个简单的扩展方法,不需要检查2465 - Can not find the Field name类型:

npm ERR! Error: EACCES, open '/Users/gtmac/.npm/npm/1.3.4/package/package.json'
npm ERR!  { [Error: EACCES, open '/Users/gtmac/.npm/npm/1.3.4/package/package.json']
npm ERR!   errno: 3,
npm ERR!   code: 'EACCES',
npm ERR!   path: '/Users/gtmac/.npm/npm/1.3.4/package/package.json',
npm ERR!   parent: 'plugman' }
npm ERR! 
npm ERR! Please try running this command again as root/Administrator.

npm ERR! System Darwin 14.3.0
npm ERR! command "node" "/usr/local/bin/npm" "install" "-g" "plugman"
npm ERR! cwd /Users/gtmac/Documents/Sinu/Insightix
npm ERR! node -v v0.10.29
npm ERR! npm -v 1.4.14
npm ERR! path /Users/gtmac/.npm/npm/1.3.4/package/package.json
npm ERR! code EACCES
npm ERR! errno 3
npm ERR! stack Error: EACCES, open '/Users/gtmac/.npm/npm/1.3.4/package/package.json'
npm ERR! 
npm ERR! Additional logging details can be found in:
npm ERR!     /Users/gtmac/Documents/Sinu/Insightix/npm-debug.log
npm ERR! not ok code 0

它也适用于可空的枚举。标准Enum方法没有,所以我创建了一个扩展来覆盖它。

public static bool HasAnyFlag(this Enum value, Enum flags)
{
    return
        value != null && ((Convert.ToInt32(value) & Convert.ToInt32(flags)) != 0);
}

一个简单的测试:

HasFlag

享受!

答案 9 :(得分:2)

这对你有用吗?

if ((letter & (Letters.A | Letters.B)) != 0)

此致

Sebastiaan

答案 10 :(得分:2)

这里有很多答案,但我认为使用Flags最常用的方法是Letters.AB.HasFlag(letter)或(Letters.A | Letters.B).HasFlag(letter)if you you还没有Letters.AB。 letter.HasFlag(Letters.AB)仅在两者兼有时才有效。

答案 11 :(得分:0)

我可以看到有两种方法可用于检查是否正在设置任何位。

Aproach A

if (letter != 0)
{
}

只要您不介意检查所有位,包括未定义的位,就可以正常工作!

Aproach B

if ((letter & Letters.All) != 0)
{
}

只检查定义的位,只要Letters.All代表所有可能的位。

对于特定位(一组或多组),使用Aproach B将Letters.All替换为您要检查的位(见下文)。

if ((letter & Letters.AB) != 0)
{
}

答案 12 :(得分:0)

您可以检查该值是否为零。

if ((Int32)(letter & Letters.AB) != 0) { }

但我认为这是一个更好的解决方案,引入一个值为零的新枚举值,并再次比较此枚举值(如果可能,因为您必须能够修改枚举)。

[Flags]
enum Letters
{
    None = 0,
    A    = 1,
    B    = 2,
    C    = 4,
    AB   =  A | B,
    All  = AB | C
}

if (letter != Letters.None) { }

<强>更新

错过了问题 - 修正了第一个建议,忽略了第二个建议。

答案 13 :(得分:0)

if((int)letter != 0) { }

答案 14 :(得分:0)

抱歉,我会在VB中显示:)

   <Flags()> Public Enum Cnt As Integer
        None = 0
        One = 1
        Two = 2
        Three = 4
        Four = 8    
    End Enum

    Sub Test()
    Dim CntValue As New Cnt
    CntValue += Cnt.One
    CntValue += Cnt.Three
    Console.WriteLine(CntValue)
    End Sub

CntValue = 5 因此枚举包含1 + 4

答案 15 :(得分:0)

对于任何类型的枚举,您都可以在枚举上使用此扩展方法:

public static bool IsSingle(this Enum value)
{
    var items = Enum.GetValues(value.GetType());
    var counter = 0;
    foreach (var item in items)
    {
        if (value.HasFlag((Enum)item))
        {
            counter++;
        }
        if (counter > 1)
        {
            return false;
        }
    }
    return true;
}

答案 16 :(得分:0)

从 .Net 4 开始,您可以使用速记版本而无需明确指定 &:

if(Letters.AB.HasFlag(Letters.C))