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?
Réponses
Trop de publicités?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
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é.)
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...
}