Как проверить, установлены ли какие-либо флаги комбинации флагов?
допустим, у меня есть это перечисление:
[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)
не могли бы вы, например, поменять &
С чем-то?
не слишком стабильно, когда дело доходит до двоичных вещей, как это...
16 ответов:
Если вы хотите знать, есть ли у letter какие-либо Буквы в AB, вы должны использовать и & оператора. Что-то вроде:
if ((letter & Letters.AB) != 0) { // Some flag (A,B or both) is enabled } else { // None of them are enabled }
в .NET 4 Вы можете использовать перечисление.Способ 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); } }
в Примере отображается следующий вывод:
// 1 of 3 families in the sample have no pets. // 2 of 3 families in the sample have a dog.
Я использую методы расширения, чтобы писать такие вещи :
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; } }
здесь HasFlag метод в .NET 4 или выше.
if(letter.HasFlag(Letters.AB)) { }
Если вы можете использовать .NET 4 или выше, чем использовать метод HasFlag ()
примеры
letter.HasFlag(Letters.A | Letters.B) // both A and B must be set
то же, что и
letter.HasFlag(Letters.AB)
Если это действительно раздражает вас, вы можете написать функцию вроде этого:
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)) { // ... }
чтобы проверить, установлен ли, например, AB, я могу сделать это:
if ((letter & Letters.АБ) = = буквы.АБ)
есть ли более простой способ проверить, установлены ли какие-либо флаги комбинированной флаговой константы, чем следующие?
это проверяет, что и 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));
Кстати, имя переменной в примере вопроса является единственной "буквой", что может означать, что она представляет собой только одну букву; пример кода дает понять, что его набор возможные буквы и что допускается несколько значений, поэтому рассмотрите переименование переменной "буквы".
Я создал простой метод расширения, который не требует проверки на
Enum
типа:public static bool HasAnyFlag(this Enum value, Enum flags) { return value != null && ((Convert.ToInt32(value) & Convert.ToInt32(flags)) != 0); }
Он также работает на nullable перечисления. Стандартный
HasFlag
метод не дает, поэтому я создал расширение, чтобы покрыть это.public static bool HasFlag(this Enum value, Enum flags) { int f = Convert.ToInt32(flags); return value != null && ((Convert.ToInt32(value) & f) == f); }
простой тест:
[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)); }
наслаждайтесь!
здесь есть много ответов, но я думаю, что самым идиоматичным способом сделать это с флагами будут буквы.АВ.HasFlag (письмо) или (письма.письмо.Си.)HasFlag (письмо), если у вас еще не было писем.АВ. письмо.HasFlag(Букв.AB) работает только в том случае, если он имеет оба.
вы могли бы просто проверить, если значение не равно нулю.
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) { }
обновление
Missread вопрос-исправлено первое предложение и просто игнорировать второе предложение.
есть два приближения, которые я вижу, что будет работать для проверки любого установленного бита.
Приблизительно A
if (letter != 0) { }
это работает до тех пор, пока вы не возражаете против проверки все биты, в том числе не определены слишком близких!
Приблизительно B
if ((letter & Letters.All) != 0) { }
Это только проверяет определенные биты, до тех пор, как буквы.Все представляет все возможные биты.
для определенных битов (один или несколько набор), использовать подход Б замена букв.Все биты, которые вы хотите проверить (см. ниже).
if ((letter & Letters.AB) != 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
вы можете использовать этот метод расширения на перечисление, для любого типа перечисления:
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; }