2009-08-27 94 views
124

比方說,我有這樣的枚舉:如何檢查標誌組合的任何標誌是否已設置?

[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) 

你能舉個例子把&換成東西嗎?

不是當談到這樣的二進制東西太穩定...

+0

不應該全部讀取'All = A | B | C'? – stevehipwell 2009-08-27 09:56:22

+4

AB | C相當於A | B | C因爲AB被定義爲A | B之前。 – 2009-08-27 10:00:14

+0

@DanielBrückner - 它是相同的,但它的可讀性較差。特別是如果枚舉擴展。 – stevehipwell 2009-08-27 10:03:49

回答

92

。類似於:

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

據我所見,這可以完成這項工作。並有最清晰的評論。雖然沒有'letter&Letters.AB'的括號沒有編譯。在那裏編輯。 – Svish 2009-08-27 10:57:22

+0

另外,如果我介紹了'Letters.None',我想你可以用'0'替換那個比較少的神奇數字? – Svish 2009-08-27 11:07:47

+0

當然。但我不認爲與0的AND比較可以被嚴格視爲一個幻數。 – yeyeyerman 2009-08-27 13:57:35

0
if((int)letter != 0) { } 
+0

您可能會和我一樣犯這樣的錯誤 - 他想檢查是否設置了A或B,但忽略了C. – 2009-08-27 10:04:29

+0

如果您要檢查enum是否爲0,則不需要轉換。 – stevehipwell 2009-08-27 10:12:16

+0

這將檢查是否有任何其中的一個是確定的,如果有任何一個聯合枚舉被設置的話。 – Svish 2009-08-27 11:03:32

3

如何

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

+0

是的!這將過濾A和B值,如果包含C,則忽略。所以如果它大於0,它也是A或B或AB。 – awe 2009-08-27 10:07:47

+3

這不是100%使用帶符號的值。由於這個原因,!= 0比> 0好。 – stevehipwell 2009-08-27 10:40:24

9

如果它真的惹惱了你,你可以寫一個這樣的功能:如果你想知道,如果信有任何AB字母必須使用AND &運營商的

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

'return'(value&flag)== flag;'不編譯:*「操作符'&'不能應用於'T'和'T''*類型的操作數。 – 2009-08-27 09:59:55

+0

你需要投入很長的第一個...... – 2009-08-27 10:05:10

+0

這並不能幫助他理解二進制操作...... – awe 2009-08-27 10:09:23

2

這會適合您嗎?

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

問候,

Sebastiaan

5

要檢查是否例如AB設爲我可以這樣做:

IF((信& Letters.AB)==快報。 AB)

是否有一種更簡單的方法來檢查組合標誌常量的任何標誌是否設置爲f ollowing?

這就驗證了 A和B被設置,並忽略任何其他標誌是否被設置。

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

這就驗證了要麼 A或B被設置,並忽略任何其他標誌是否被設定或沒有。

這可以簡化爲:

if(letter & Letters.AB) 

這裏的下二進制操作;它應該是簡單的將其應用到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)); 

順便說一下,在問題的例子變量的命名是奇異「信」,這可能意味着它僅代表一個字母;示例代碼清楚地表明它是一組可能的字母,並且允許多個值,因此可以考慮重命名變量「字母」。

+0

「anything_and_a」,「a_and_or_c_and_anything_else」和「both_ac_and_anything_else」總是成立嗎?或者我在這裏錯過了什麼? – Svish 2009-08-27 10:21:59

+0

在這種情況下,您可以看到已經初始化了哪些標誌。但是,如果標誌不包含A,那麼(flags&A)將爲0,這是錯誤的。 both_ac_and_anything_else確保A和C都被設置,但忽略任何其他也被設置的標誌(例如,B是否被設置爲真)。 – Will 2009-08-27 10:28:09

+0

嗯,其中一些以C#編號結尾,而不是布爾值。你會如何將它們轉換爲布爾值? – Svish 2009-08-27 10:54:08

0

您可以檢查該值是否不爲零。

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

但我會認爲這是一個更好的解決方案與零值引入新的枚舉值和比較agains此枚舉值(如果可能的話,因爲你必須能夠修改枚舉)。

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

if (letter != Letters.None) { } 

UPDATE

Missread問題 - 固定的第一個建議,只是忽略了第二個建議。

+0

如果您要檢查enum是否爲0,則不需要轉換。 – stevehipwell 2009-08-27 10:11:46

0

我可以看到有兩個方法可以用來檢查設置的位。

阿布羅奇一個

if (letter != 0) 
{ 
} 

這個工作,只要你不介意檢查所有位,包括非義之輩!

阿布羅奇乙

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

這僅檢查所定義的比特,只要Letters.All表示所有可能的位。

對於特定位(一個或多個集合),使用Aproach B替換Letters.All,並使用要檢查的位(請參見下文)。

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

您可能會和我一樣犯這個錯誤 - 他想檢查A或B是否已設置,但忽略C. – 2009-08-27 10:03:26

54

我使用擴展方法來寫這樣的事情:

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

您可以使其更緊一點像這樣:'T:struct,IConvertible'。好的代碼否則! – 2012-04-20 22:45:29

+0

@HamishGrubijan,好點...枚舉也實現IFormattable和IComparable。但是,所有的數字類型也都實現了這些接口,所以它們不足以排除它們。 – 2012-04-20 23:09:32

+3

注意:它使分配 – 2012-06-20 09:08:14

122

在.NET 4中,你可以使用Enum.HasFlag method

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. 
+7

這沒有解決OP問題。您仍然必須&&多個HasFlag操作來確定是否設置了*任何*標誌。所以問題是'petsInFamily'有一個'Pet.Dog ||「 Pet.Cat'? – GoClimbColorado 2015-09-04 01:23:04

+0

請參閱Skeet先生的明確答案... [HasFlags Multiple](http://stackoverflow.com/a/7984448/42239) – GoClimbColorado 2015-09-04 01:29:51

20

在.NET 4或更高版本中有HasFlag方法。

if(letter.HasFlag(Letters.AB)) 
{ 
} 
14

如果可以使用.NET 4或高於使用HasFlag()方法

例子

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

相同

letter.HasFlag(Letters.AB) 
1

我創建了一個簡單的擴展方法,該方法不需要檢查Enum類型:

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

它也適用於可爲空的枚舉。標準的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)); 
} 

享受!

2

這裏有很多答案,但我認爲用Flags做這個的最習慣的方法是Letters.AB.HasFlag(letter)或(Letters.A | Letters.B).HasFlag(letter)if你還沒有Letters.AB。 letter.HasFlag(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

0

您可以使用此枚舉擴展方法,對於任何類型的枚舉:

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; 
} 
相關問題