154 votes

Itérer sur les valeurs dans Flags Enum?

Si j'ai une variable contenant une énumération de drapeaux, puis-je en quelque sorte itérer sur les valeurs de bits de cette variable spécifique? Ou dois-je utiliser Enum.GetValues ​​pour parcourir l'ensemble de l'énumération et vérifier celles qui sont définies?

200voto

Greg Points 11248
static IEnumerable<Enum> GetFlags(Enum input)
{
    foreach (Enum value in Enum.GetValues(input.GetType()))
        if (input.HasFlag(value))
            yield return value;
}

66voto

agritton Points 59

Voici une solution Linq au problème.

 public static IEnumerable<Enum> GetFlags(this Enum e)
{
      return Enum.GetValues(e.GetType()).Cast<Enum>().Where(e.HasFlag);
}
 

41voto

Jeff Mercado Points 42075

Il n'y a pas toutes les méthodes autant que je sache, pour obtenir à chaque composant. Voici une façon que vous pouvez obtenir:

[Flags]
enum Items
{
    None = 0x0,
    Foo  = 0x1,
    Bar  = 0x2,
    Baz  = 0x4,
    Boo  = 0x6,
}

var value = Items.Foo | Items.Bar;
var values = value.ToString()
                  .Split(new[] { ", " }, StringSplitOptions.None)
                  .Select(v => (Items)Enum.Parse(typeof(Items), v));

// This method will always end up with the most applicable values
value = Items.Bar | Items.Baz;
values = value.ToString()
              .Split(new[] { ", " }, StringSplitOptions.None)
              .Select(v => (Items)Enum.Parse(typeof(Items), v)); // Boo

J'ai adapté ce Enum n'en interne pour générer la chaîne de caractères à la place, retourner les drapeaux. Vous pouvez regarder le code dans le réflecteur et devrait être plus ou moins équivalent. Fonctionne bien pour un usage général les cas où il y a des valeurs qui contiennent plusieurs bits.

static class EnumExtensions
{
    public static IEnumerable<Enum> GetFlags(this Enum value)
    {
        return GetFlags(value, Enum.GetValues(value.GetType()).Cast<Enum>().ToArray());
    }

    public static IEnumerable<Enum> GetIndividualFlags(this Enum value)
    {
        return GetFlags(value, GetFlagValues(value.GetType()).ToArray());
    }

    private static IEnumerable<Enum> GetFlags(Enum value, Enum[] values)
    {
        ulong bits = Convert.ToUInt64(value);
        List<Enum> results = new List<Enum>();
        for (int i = values.Length - 1; i >= 0; i--)
        {
            ulong mask = Convert.ToUInt64(values[i]);
            if (i == 0 && mask == 0L)
                break;
            if ((bits & mask) == mask)
            {
                results.Add(values[i]);
                bits -= mask;
            }
        }
        if (bits != 0L)
            return Enumerable.Empty<Enum>();
        if (Convert.ToUInt64(value) != 0L)
            return results.Reverse<Enum>();
        if (bits == Convert.ToUInt64(value) && values.Length > 0 && Convert.ToUInt64(values[0]) == 0L)
            return values.Take(1);
        return Enumerable.Empty<Enum>();
    }

    private static IEnumerable<Enum> GetFlagValues(Type enumType)
    {
        ulong flag = 0x1;
        foreach (var value in Enum.GetValues(enumType).Cast<Enum>())
        {
            ulong bits = Convert.ToUInt64(value);
            if (bits == 0L)
                //yield return value;
                continue; // skip the zero value
            while (flag < bits) flag <<= 1;
            if (flag == bits)
                yield return value;
        }
    }
}

La méthode d'extension GetIndividualFlags() obtient tous les drapeaux pour un type. De sorte que les valeurs contenant plusieurs bits sont laissés de côté.

var value = Items.Bar | Items.Baz;
value.GetFlags();           // Boo
value.GetIndividualFlags(); // Bar, Baz

30voto

RobinHood70 Points 543

De retour quelques années plus tard, avec un peu plus d'expérience, ma réponse ultime pour un seul bit valeurs que, le déplacement du bit de poids faible à la plus élevée bits, est une légère variante de Jeff Mercado intérieure de la routine:

public static IEnumerable<Enum> GetUniqueFlags(this Enum flags)
{
    var flag = 1;
    foreach (var value in Enum.GetValues(flags.GetType()).Cast<Enum>())
    {
        ulong bits = Convert.ToUInt64(value);
        while (flag < bits)
        {
            flag <<= 1;
        }

        if (flag == bits && flags.HasFlag(value))
        {
            yield return value;
        }
    }
}

Il semble fonctionner, et malgré mes objections, il y a quelques années, j'utilise HasFlag ici, car il est beaucoup plus lisible que d'utiliser des comparaisons au niveau du bit et de la différence de vitesse est minimale pour tout ce que je vais faire. (Il est tout à fait possible qu'ils l'ont amélioré la vitesse de HasFlags depuis puis de toute façon, pour tout ce que je sais...je n'ai pas testé.)

2voto

Dr TJ Points 1042

Vous n'avez pas besoin de parcourir toutes les valeurs. Il suffit de vérifier vos drapeaux spécifiques comme ceci:

 if((myVar & FlagsEnum.Flag1) == FlagsEnum.Flag1) 
{
   //do something...
}
 

ou (comme pstrjds l'a dit dans les commentaires), vous pouvez vérifier s'il est utilisé comme ceci :

 if(myVar.HasFlag(FlagsEnum.Flag1))
{
   //do something...
}
 

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