अगर झंडे के संयोजन के किसी भी झंडे को सेट किया गया है तो कैसे जांचें?


180

मान लें कि मेरे पास यह एनम है:

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

आप उदाहरण के लिए &कुछ के साथ स्वैप कर सकते हैं ?

बहुत स्थिर नहीं है जब यह इस तरह से बाइनरी सामान की बात आती है ...


सभी को नहीं पढ़ना चाहिए 'ऑल = ए | B | सी'?
स्टेविपवेल

4
एबी | C, A के बराबर है B | C क्योंकि AB को A के रूप में परिभाषित किया गया था इससे पहले बी।
डैनियल ब्रुकनर

1
@ डैनियल ब्रुकनर - यह समकक्ष है, लेकिन यह कम पठनीय है। खासकर अगर एनम का विस्तार किया गया था।
स्टेविपवेल

सच। मैं इसे बेहतर पढ़ने के लिए बदल सकता हूं।
शविश

जवाबों:


145

यदि आप जानना चाहते हैं कि क्या पत्र में AB का कोई अक्षर है, तो आपको AND & ऑपरेटर का उपयोग करना होगा । कुछ इस तरह:

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

2
जहां तक ​​मैं देख सकता हूं, यह काम करता है। और स्पष्ट टिप्पणियाँ थी। चारों ओर कोष्ठक के बिना संकलन नहीं करता है letter & Letters.AB। वहाँ में संपादित किया।
Svish

इसके अलावा, अगर मैंने एक परिचय दिया Letters.None, तो मुझे लगता है कि आप 0एक कम तुलना-के साथ-जादू-नंबर लुक के लिए स्वैप कर सकते हैं?
Svish

बेशक। लेकिन मुझे नहीं लगता कि और 0 के साथ तुलना को जादू की संख्या के रूप में सख्ती से सोचा जा सकता है।
येयेरमैन

9
इसके अलावा stackoverflow.com/questions/40211/how-to-compare-flags-in-c एक अनुशंसित उत्तर है क्योंकि यह विचाराधीन मद के खिलाफ जांच के रूप में अगर यह 0 के बराबर है
dan richardson

@danrichardson सटीक आइटम के लिए चेक के साथ समस्या यह है कि यह उस मामले को समाप्त कर देता है जब यौगिक मूल्य का एक हिस्सा सेट किया जाता है (या तो ए, या बी), जो ओपी नहीं चाहता है।
टॉम

181

.NET 4 में आप 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()
   {
      // 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.

14
यह ओपी प्रश्न को संबोधित नहीं करता है। यदि कोई फ़्लैग सेट किया जाता है, तो यह निर्धारित करने के लिए आपको कई और हस्फ़्लैग संचालन & अभी भी होने चाहिए । तो सवाल यह है petsInFamilyकि या तो एक है Pet.Dog || Pet.Cat?
GoClimbColorado

1
श्री स्कीट का स्पष्ट उत्तर देखें ... HasFlags Multiple
GoClimbColorado

59

मैं चीजों को लिखने के लिए एक्सटेंशन विधियों का उपयोग करता हूं:

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
आप इसे थोड़ा तंग कर सकते हैं जैसे where T : struct, IConvertible:। महान कोड अन्यथा!
हमीश ग्रुबीजन

@ HamishGrubijan, अच्छी बात ... और enums भी IFormattable और IComparable को लागू करते हैं। हालांकि, सभी संख्यात्मक प्रकार उन इंटरफेस को भी लागू करते हैं, इसलिए उन्हें बाहर करने के लिए पर्याप्त नहीं है
थॉमस लेवेस्क

साझा करने के लिए धन्यवाद, लेकिन आपको हमेशा एनम की जांच करने की आवश्यकता नहीं है। IsFlagSet(this Enum value, Enum flag)काफी है।
djmj

34

नहीं है HasFlag नेट 4 या उच्चतर में विधि।

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

26

यदि आप .NET 4 या उपयोग से अधिक का उपयोग कर सकते हैं HasFlag () विधि

उदाहरण

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

के समान

letter.HasFlag(Letters.AB)

क्या आप सुनिश्चित हैं कि bitwise ORयह "दोनों सेट होना चाहिए" और कोई नहीं?
ब्रैकेट्स

1
bitwise ORमानों को संयोजित करेगा, इसलिए 1000 | 0010 1010 हो जाता है, या दोनों सेट
आर्मंडो

13

यदि यह वास्तव में आपको गुस्सा दिलाता है, तो आप इस तरह एक समारोह लिख सकते हैं:

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

1
रेखा return (value & flag) == flag;संकलित नहीं करती है: "ऑपरेटर" और 'टाइप' टी 'और' टी 'के ऑपरेंड पर लागू नहीं किया जा सकता है
फ्रेड्रिक मोर्क

1
खौफ: सवाल बाइनरी ऑपरेशंस के बारे में नहीं था, सवाल C # में बिटमैस्क-संबंधित ऑपरेशंस के सिंटैक्स को सरल बनाने के बारे में था। स्टैकओवरफ्लो पर बहुत सारे उत्कृष्ट बाइनरी ऑपरेशन से संबंधित प्रश्न और उत्तर पहले से ही हैं, उन्हें हर जगह रीपोस्ट करने की आवश्यकता नहीं है।
तमस Czinege

मुझे सुझाव देना चाहिए कि बाइनरी ऑपरेशन से अपरिचित लोग परिचित हो जाते हैं, क्योंकि इसे ऊपर छिपाने के लिए मचान वास्तव में मेरी राय में बहुत कम पठनीय बनाता है। निश्चित रूप से नीचे मेरा 'कच्चा' समाधान वर्तमान में इस समाधान के स्कोर की तुलना में इतना अच्छा नहीं कर रहा है, इसलिए लोग अपनी वरीयताओं को वोट कर रहे हैं और मुझे इसका सम्मान करना
होगा

10

यह देखने के लिए कि क्या उदाहरण के लिए AB सेट है मैं यह कर सकता हूं:

यदि ((पत्र और पत्र ।AB) == पत्र.AB)

क्या यह जाँचने का एक सरल तरीका है कि संयुक्त ध्वज स्थिरांक के किसी भी झंडे को निम्नलिखित की तुलना में सेट किया गया है या नहीं?

यह जाँचता है कि 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));

संयोग से, प्रश्न के उदाहरण में चर का नाम विलक्षण 'अक्षर' है, जिसका अर्थ यह हो सकता है कि यह केवल एक अक्षर का प्रतिनिधित्व करता है; उदाहरण कोड यह स्पष्ट करता है कि इसके संभावित अक्षरों का एक सेट और कई मानों की अनुमति है, इसलिए चर 'अक्षरों' का नाम बदलने पर विचार करें।


नहीं होगा anything_and_a, a_and_or_c_and_anything_elseऔर both_ac_and_anything_elseहमेशा सच होगा? या मुझसे यहां कुछ छूट रहा है?
श्वेत

इस मामले में, आप देख सकते हैं कि किन झंडों को आरम्भ किया गया है। हालांकि, झंडे में ए नहीं होना चाहिए, फिर (झंडे और ए) 0 होगा, जो गलत है। दोनों_ac_and_anything_else सुनिश्चित करता है कि A और C दोनों सेट हैं, लेकिन सेट किए गए किसी भी अन्य झंडे को अनदेखा करता है (उदाहरण के लिए कि क्या B सेट है या नहीं)।
विल

हम्म, उन में से कुछ संख्या के रूप में समाप्त होता है और सी # में बूलियन नहीं है। आप उन्हें बूलियन में कैसे बदलेंगे?
शविश

यह आपके लिए अंतर्निहित रूप से रूपांतरित नहीं है? शून्य equ असत्य ’के बराबर है, और अन्य सभी मूल्य। सत्य’ हैं।
विल

4

कैसा रहेगा

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

?


हाँ! यह ए और बी मूल्यों पर फ़िल्टर करेगा, और अगर सी को शामिल किया गया है, तो इसे अनदेखा करें। तो अगर यह> 0 है, तो यह A या B या AB भी है।
भय

3
यह 100% हस्ताक्षरित मूल्यों के साथ काम नहीं करता है। ! = 0 इस कारण से> 0 से बेहतर है।
स्टीववेवेल

4

मैंने एक सरल एक्सटेंशन विधि बनाई, जिसमें 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));
}

का आनंद लें!


4

यहाँ पर बहुत सारे उत्तर हैं, लेकिन मुझे लगता है कि झंडे के साथ ऐसा करने का सबसे मुहावरेदार तरीका होगा पत्र .AB.HasFlag (पत्र) या (पत्र। | पत्र। B) ।HasFlag (पत्र) यदि आपने नहीं किया है। पहले से ही पत्र .AB। letter.HasFlag (Letters.AB) केवल तभी काम करता है जब इसमें दोनों हों।



1

आप इस विस्तार विधि का प्रयोग किसी भी प्रकार के एनम के लिए कर सकते हैं:

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

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

आपकी वही गलती हो सकती है जो मैंने की थी - वह यह जाँचना चाहता है कि ए या बी सेट है या नहीं। सी को अनदेखा करें
डैनियल ब्रुकनर

अगर आपको
०.१ के

यह जांच करेगा कि क्या उनमें से कोई भी सेट किया गया है, न कि अगर कोई संयुक्त एनम सेट किया गया है।
Svish

0

यदि मूल्य शून्य नहीं है, तो आप जांच कर सकते हैं।

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

अपडेट करें

प्रश्न को याद रखें - पहले सुझाव को निर्धारित किया और दूसरे सुझाव को अनदेखा किया।


अगर आपको
०.१ के

0

वहाँ दो aproaches है कि मैं देख सकता हूँ कि किसी भी बिट सेट के लिए जाँच के लिए काम करेगा।

Aproach A

if (letter != 0)
{
}

यह तब तक काम करता है जब तक कि आप सभी बिट्स के लिए जाँच नहीं करते , गैर-परिभाषित लोगों सहित भी!

Aproach B

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

यह केवल परिभाषित बिट्स की जांच करता है, जब तक कि पत्र। सभी संभव बिट्स का प्रतिनिधित्व करते हैं।

विशिष्ट बिट्स (एक या अधिक सेट) के लिए, Aproach B की जगह लेटर्स का उपयोग करें। उन सभी बिट्स के साथ जिन्हें आप जांचना चाहते हैं (नीचे देखें)।

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

आपकी वही गलती हो सकती है जैसी मैंने की थी - वह यह जाँचना चाहता है कि ए या बी सेट है या नहीं। सी को अनदेखा करें
डैनियल ब्रुकनर 27'09

-1

क्षमा करें, लेकिन मैं इसे 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 तो Enum में 1 + 4 होता है

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.