225 votes

C #: Comment vérifier si des indicateurs d'une combinaison d'indicateurs 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 ...

218voto

Chuck Kasabula Points 321

Dans .NET 4, vous pouvez utiliser la 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()
   {
      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);   
   }
}
// The example displays the following output: 
//       1 of 3 families in the sample have no pets. 
//       2 of 3 families in the sample have a dog.
 

181voto

yeyeyerman Points 2460

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

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

67voto

Thomas Levesque Points 141081

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

14voto

Tamas Czinege Points 49277

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

11voto

Will Points 30630

Pour vérifier si, par exemple, AB est réglé je peux faire ceci:

si((lettre et les Lettres.AB) == les Lettres.AB)

Est-il un moyen plus simple de vérifier si l'un des drapeaux d'un drapeau constantes sont définies de la suivante?

Cette vérification, qui à la fois A et B sont ensemble, et ignore si tous les autres drapeaux sont mis.

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

Cette opération vérifie que ce soit A ou B est défini, et ignore si tous les autres drapeaux sont ensemble ou pas.

Cela peut être simplifiée:

if(letter & Letters.AB)

Voici le C pour les opérations binaires; il devrait être très simple à appliquer ce que 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));

D'ailleurs, le nom de la variable dans la question de l'exemple est le singulier de "lettre", ce qui pourrait impliquer qu'il ne représente qu'une seule lettre; l'exemple de code précise que sa de lettres et que les valeurs multiples sont autorisées, d'envisager de renommer la variable de "lettres".

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X