web-dev-qa-db-fra.com

Comment vérifier si des drapeaux d'une combinaison de drapeaux sont définis?

Disons que j'ai cette énumération:

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

Pour vérifier si, par exemple, AB est défini, je peux le faire:

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

Existe-t-il un moyen plus simple de vérifier si l'un des indicateurs d'une constante d'indicateur combinée est défini par rapport au suivant?

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

Pourriez-vous par exemple échanger le & avec quelque chose?

Pas trop stable quand il s'agit de binaires comme ça ...

162
Svish

Si vous voulez savoir si une lettre a une de ces lettres dans AB, vous devez utiliser l'opérateur ET &. Quelque chose comme:

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

Dans .NET 4, vous pouvez utiliser la méthode méthode Enum.HasFlag :

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

L'exemple affiche la sortie suivante:

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

J'utilise des méthodes d'extension pour écrire des choses comme ça:

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

Voici le code:

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;
    }
}
57
Thomas Levesque

Il existe une méthode HasFlag dans .NET 4 ou supérieure.

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

Si vous pouvez utiliser .NET 4 ou une version ultérieure, utilisez la méthode HasFlag ()

exemples

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

pareil que

letter.HasFlag(Letters.AB)
19
Luka

Si cela vous ennuie vraiment, vous pouvez écrire une fonction comme celle-ci:

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))
{
   // ...
}
11
Tamas Czinege

Pour vérifier si, par exemple, AB est défini, je peux le faire:

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

Existe-t-il un moyen plus simple de vérifier si l'un des indicateurs d'une constante d'indicateur combinée est défini par rapport au suivant?

Ceci vérifie que les deux A et B sont définis et ignore si d'autres indicateurs sont définis.

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

Ceci vérifie que soit A ou B est défini, et ignore si d'autres indicateurs sont définis ou non.

Ceci peut être simplifié pour:

if(letter & Letters.AB)

Voici le C pour les opérations binaires; il devrait être simple de l'appliquer à 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));

Incidemment, la dénomination de la variable dans l'exemple de la question est la "lettre" singulière, ce qui pourrait impliquer qu'elle ne représente qu'une seule lettre; L'exemple de code indique clairement qu'il s'agit d'un ensemble de lettres possibles et que plusieurs valeurs sont autorisées. Il est donc conseillé de renommer la variable 'lettres'.

7
Will

J'ai créé une méthode d'extension simple qui ne nécessite pas de vérification sur les types Enum:

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

Cela fonctionne également sur les énumérations nullables. La méthode standard HasFlag ne fonctionne pas, alors j'ai créé une extension pour couvrir cela aussi.

public static bool HasFlag(this Enum value, Enum flags)
{
    int f = Convert.ToInt32(flags);

    return
        value != null && ((Convert.ToInt32(value) & f) == f);
}

Un test simple:

[Flags]
enum Option
{
    None = 0x00,
    One = 0x01,
    Two = 0x02,
    Three = One | Two,
    Four = 0x04
}

[TestMethod]
public void HasAnyFlag()
{
    Option o1 = Option.One;
    Assert.AreEqual(true, o1.HasAnyFlag(Option.Three));
    Assert.AreEqual(false, o1.HasFlag(Option.Three));

    o1 |= Option.Two;
    Assert.AreEqual(true, o1.HasAnyFlag(Option.Three));
    Assert.AreEqual(true, o1.HasFlag(Option.Three));
}

[TestMethod]
public void HasAnyFlag_NullableEnum()
{
    Option? o1 = Option.One;
    Assert.AreEqual(true, o1.HasAnyFlag(Option.Three));
    Assert.AreEqual(false, o1.HasFlag(Option.Three));

    o1 |= Option.Two;
    Assert.AreEqual(true, o1.HasAnyFlag(Option.Three));
    Assert.AreEqual(true, o1.HasFlag(Option.Three));
}

Prendre plaisir!

3

Que diriez-vous

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

?

3
Jakob Christensen

Est-ce que cela fonctionnerait pour vous?

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

Cordialement,

Sebastiaan

2
Sebastiaan M

Il y a beaucoup de réponses ici mais je pense que la manière la plus idiomatique de faire cela avec Flags serait Letters.AB.HasFlag (lettre) ou (Letters.A | Letters.B) .HasFlag (lettre) si vous ne le faites pas avez déjà Letters.AB. letter.HasFlag (Letters.AB) ne fonctionne que s'il possède les deux.

2
Novaterata

Désolé, mais je vais le montrer dans 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 Donc l'énumération contient 1 + 4

0
Wiroko

Vous pouvez utiliser cette méthode d'extension sur enum, pour tout type d'ensums:

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;
}
0
masehhat
if((int)letter != 0) { }
0
Lee

Vous pouvez simplement vérifier si la valeur n'est pas zéro.

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

Mais je considérerais que la meilleure solution consiste à introduire une nouvelle valeur d’énumération avec la valeur zéro et à comparer cette valeur d’énumération (si possible car vous devez pouvoir modifier l’énumération).

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

if (letter != Letters.None) { }

MISE À JOUR

Erreur de lecture de la question - corrige la première suggestion et ignore simplement la seconde.

0
Daniel Brückner

Je peux voir qu'il y a deux approches qui fonctionneraient pour vérifier si un bit est défini.

aproach A

if (letter != 0)
{
}

Cela fonctionne tant que vous n'avez pas peur de vérifier tous les bits, y compris ceux qui ne sont pas définis!

aproach B

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

Cela ne vérifie que les bits définis, tant que Letters.All représente tous les bits possibles.

Pour des bits spécifiques (un ou plusieurs jeux), utilisez Aproach B en remplaçant Letters.All par les bits que vous souhaitez vérifier (voir ci-dessous).

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