एनम पर सबसे आम सी # बिटवाइज ऑपरेशन


201

मेरे जीवन के लिए, मुझे याद नहीं है कि बिटफील्ड में कैसे सेट, डिलीट, टॉगल या टेस्ट किया जा सकता है। या तो मैं अनिश्चित हूं या मैं उन्हें मिलाता हूं क्योंकि मुझे शायद ही कभी इनकी आवश्यकता होती है। तो "बिट-चीट-शीट" अच्छा होगा।

उदाहरण के लिए:

flags = flags | FlagsEnum.Bit4;  // Set bit 4.

या

if ((flags & FlagsEnum.Bit4)) == FlagsEnum.Bit4) // Is there a less verbose way?

क्या आप अन्य सभी सामान्य परिचालनों के उदाहरण दे सकते हैं, अधिमानतः एक [झंडे] एनम का उपयोग करके सी # सिंटैक्स में?


5
इसका उत्तर यहां
ग्रेग रोजर्स

7
बहुत बुरा है कि लिंक इस विषय के लिए प्रश्न संकेत में प्रकट नहीं होता है।
cori

10
यह प्रश्न c / c ++ के लिए टैग किया गया है, हालांकि, C # के बारे में जानकारी खोजने वाला कोई व्यक्ति संभवतः सिंटैक्स के समान प्रतीत होने पर भी वहां नहीं दिखेगा।
एडम लाससेक

मैं थोड़ा परीक्षण करने के लिए एक कम क्रिया तरीके से अवगत नहीं हूं
एंडी जॉनसन

2
@ और, अब .NET 4 में बिट टेस्ट के लिए एपीआई है।
आकर्षित नोक

जवाबों:


288

मैंने इन एक्सटेंशन पर कुछ और काम किया - आप यहां कोड पा सकते हैं

मैंने कुछ विस्तार विधियाँ लिखी हैं जो System.Enum को विस्तारित करती हैं जो मैं अक्सर उपयोग करता हूं ... मैं यह दावा नहीं कर रहा कि वे बुलेटप्रूफ हैं, लेकिन उन्होंने मदद की है ... टिप्पणियाँ हटा दी गईं ...

namespace Enum.Extensions {

    public static class EnumerationExtensions {

        public static bool Has<T>(this System.Enum type, T value) {
            try {
                return (((int)(object)type & (int)(object)value) == (int)(object)value);
            } 
            catch {
                return false;
            }
        }

        public static bool Is<T>(this System.Enum type, T value) {
            try {
                return (int)(object)type == (int)(object)value;
            }
            catch {
                return false;
            }    
        }


        public static T Add<T>(this System.Enum type, T value) {
            try {
                return (T)(object)(((int)(object)type | (int)(object)value));
            }
            catch(Exception ex) {
                throw new ArgumentException(
                    string.Format(
                        "Could not append value from enumerated type '{0}'.",
                        typeof(T).Name
                        ), ex);
            }    
        }


        public static T Remove<T>(this System.Enum type, T value) {
            try {
                return (T)(object)(((int)(object)type & ~(int)(object)value));
            }
            catch (Exception ex) {
                throw new ArgumentException(
                    string.Format(
                        "Could not remove value from enumerated type '{0}'.",
                        typeof(T).Name
                        ), ex);
            }  
        }

    }
}

फिर उनका उपयोग निम्नलिखित की तरह किया जाता है

SomeType value = SomeType.Grapes;
bool isGrapes = value.Is(SomeType.Grapes); //true
bool hasGrapes = value.Has(SomeType.Grapes); //true

value = value.Add(SomeType.Oranges);
value = value.Add(SomeType.Apples);
value = value.Remove(SomeType.Grapes);

bool hasOranges = value.Has(SomeType.Oranges); //true
bool isApples = value.Is(SomeType.Apples); //false
bool hasGrapes = value.Has(SomeType.Grapes); //false

1
मुझे यह उपयोगी भी लगा - कोई भी विचार कि मैं इसे कैसे संशोधित कर सकता हूं ताकि यह किसी भी अंतर्निहित प्रकार पर काम करे?
चार्ली साल्ट

7
इन एक्सटेंशनों ने मेरा दिन, मेरा सप्ताह, मेरा महीना और संभवतः मेरा वर्ष बना दिया।
thaBadDawg

धन्यवाद! हर कोई: Hugoware ने जिस अपडेट को लिंक किया है, उसकी जांच करना सुनिश्चित करें।
हेल्ज क्लेन

एक्सटेंशन का एक बहुत अच्छा सेट। यह शर्म की बात है कि उन्हें मुक्केबाजी की आवश्यकता है, हालांकि मैं एक विकल्प के बारे में नहीं सोच सकता जो मुक्केबाजी का उपयोग नहीं करता है और यह सफल है। यहां तक ​​कि नई HasFlagपद्धति पर Enumमुक्केबाजी की आवश्यकता है।
ड्रू नोक

4
@Drew: देखें code.google.com/p/unconstrained-melody मुक्केबाजी से बचने का एक तरीका है :) के लिए
जॉन स्कीट

109

.NET 4 में अब आप लिख सकते हैं:

flags.HasFlag(FlagsEnum.Bit4)

4
+1 यह इंगित करने के लिए कि यद्यपि FlagsEnumएक बदसूरत नाम है। :)
जिम शूबर्ट

2
@ जीम, शायद। यह केवल एक नमूना नाम है, जैसा कि मूल प्रश्न में उपयोग किया गया है, इसलिए आप इसे अपने कोड में बदलने के लिए स्वतंत्र हैं।
ड्रू नोक जूल

14
मुझे पता है! लेकिन बदसूरत नाम IE6 की तरह हैं और शायद कभी दूर नहीं जाएंगे :(
जिम शूबर्ट

5
@JimSchubert, फिर से, मैंने मूल प्रश्न से केवल टाइप नाम को पुन: प्रस्तुत किया ताकि समस्या को भ्रमित न किया जा सके। नेट गणन प्रकार नाम रखने हेतु निर्देश संकेत मिलता है कि सभी [Flags]enums pluralised नाम है, इसलिए नाम होना चाहिए FlagsEnumकुरूपता से भी अधिक गंभीर मुद्दे हैं।
ड्रू नोक

1
मैं फ्रेमवर्क डिज़ाइन दिशानिर्देशों की भी सिफारिश करता हूं : कन्वेंशन, मुहावरे, और पुन: प्रयोज्य .NET पुस्तकालयों के लिए पैटर्न । यह खरीदना थोड़ा महंगा है, लेकिन मेरा मानना ​​है कि सफारी ऑनलाइन और बुक्स 24x7 दोनों ही इसे ग्राहकों के लिए पेश करते हैं।
जिम शूबर्ट

89

मुहावरा बिट्स या बराबर ऑपरेटर को बिट्स सेट करने के लिए उपयोग करना है:

flags |= 0x04;

एक बिट को साफ करने के लिए, मुहावरे का उपयोग बिटवाइज और नकार के साथ करना है:

flags &= ~0x04;

कभी-कभी आपके पास एक ऑफसेट होता है जो आपके बिट की पहचान करता है, और फिर मुहावरे को बाएं-शिफ्ट के साथ इनका उपयोग करना है:

flags |= 1 << offset;
flags &= ~(1 << offset);

22

@Drew

ध्यान दें कि सरलतम मामलों को छोड़कर, Enum.HasFlag कोड को मैन्युअल रूप से लिखने की तुलना में भारी प्रदर्शन जुर्माना करता है। निम्नलिखित कोड पर विचार करें:

[Flags]
public enum TestFlags
{
    One = 1,
    Two = 2,
    Three = 4,
    Four = 8,
    Five = 16,
    Six = 32,
    Seven = 64,
    Eight = 128,
    Nine = 256,
    Ten = 512
}


class Program
{
    static void Main(string[] args)
    {
        TestFlags f = TestFlags.Five; /* or any other enum */
        bool result = false;

        Stopwatch s = Stopwatch.StartNew();
        for (int i = 0; i < 10000000; i++)
        {
            result |= f.HasFlag(TestFlags.Three);
        }
        s.Stop();
        Console.WriteLine(s.ElapsedMilliseconds); // *4793 ms*

        s.Restart();
        for (int i = 0; i < 10000000; i++)
        {
            result |= (f & TestFlags.Three) != 0;
        }
        s.Stop();
        Console.WriteLine(s.ElapsedMilliseconds); // *27 ms*        

        Console.ReadLine();
    }
}

10 मिलियन से अधिक पुनरावृत्तियों, मानक बिटवाइज़ कार्यान्वयन के लिए 27 एमएस की तुलना में, HasFlags विस्तार विधि में 4793 एमएस होता है।


10
जबकि निश्चित रूप से दिलचस्प और इंगित करने के लिए अच्छा है। आपको उपयोग पर विचार करने की आवश्यकता है। इसके अनुसार यदि आप एक-दो हजार या अधिक ऑप्स नहीं कर रहे हैं तो आप शायद इस पर ध्यान भी नहीं देंगे।
जोशुआ हेस

7
HasFlagविधि मुक्केबाजी / unboxing शामिल है, जो इस अंतर के लिए खातों। लेकिन लागत इतनी तुच्छ (0.4µs) है कि जब तक आप एक तंग पाश में नहीं होते हैं, मैं किसी भी दिन अधिक पठनीय (और कम संभावना छोटी गाड़ी) घोषणात्मक एपीआई कॉल ले लूंगा।
ड्रू नोक

8
उपयोग के आधार पर, यह एक मुद्दा हो सकता है। और जब से मैं लोडर के साथ बहुत कम काम करता हूं, मुझे लगा कि यह इंगित करना अच्छा है।
चक दे

11

.NET का अंतर्निहित फ्लैग एनम ऑपरेशन दुर्भाग्य से काफी सीमित है। अधिकांश समय उपयोगकर्ताओं को बिटवाइज ऑपरेशन लॉजिक का पता लगाने के लिए छोड़ दिया जाता है।

.NET 4 में, विधि HasFlagजोड़ा गया था Enumजो उपयोगकर्ता के कोड को सरल बनाने में मदद करता है लेकिन दुर्भाग्य से इसके साथ कई समस्याएं हैं।

  1. HasFlag टाइप-सुरक्षित नहीं है क्योंकि यह किसी भी प्रकार के एनम वैल्यू तर्क को स्वीकार करता है, न कि दिए गए एनम टाइप को।
  2. HasFlagयह मानने के लिए अस्पष्ट है कि क्या यह जाँच करता है कि मान में सभी या किसी भी झंडे के लिए मूल्य गणना तर्क द्वारा प्रदान की गई है। यह सब वैसे ही है।
  3. HasFlag बल्कि धीमा है क्योंकि इसमें मुक्केबाजी की आवश्यकता होती है जो आवंटन का कारण बनता है और इस प्रकार अधिक कचरा संग्रह होता है।

फ्लैग एनमों के लिए .NET .NET के सीमित समर्थन के कारण मैंने OSS लाइब्रेरी Enums.NET को लिखा, जो इनमें से प्रत्येक मुद्दे को संबोधित करता है और फ्लैग एनमों से निपटना बहुत आसान बनाता है।

नीचे कुछ संचालन दिए गए हैं, जो सिर्फ .NET फ्रेमवर्क का उपयोग करके अपने समकक्ष कार्यान्वयन के साथ प्रदान करते हैं।

झंडे मिलाएं

नेट             flags | otherFlags

Enums.NET flags.CombineFlags(otherFlags)


झंडे हटाओ

नेट             flags & ~otherFlags

Enums.NET flags.RemoveFlags(otherFlags)


आम झंडे

नेट             flags & otherFlags

Enums.NET flags.CommonFlags(otherFlags)


झंडे गाड़ दो

नेट             flags ^ otherFlags

Enums.NET flags.ToggleFlags(otherFlags)


सभी झंडे हैं

.नेट             (flags & otherFlags) == otherFlags याflags.HasFlag(otherFlags)

Enums.NET flags.HasAllFlags(otherFlags)


किसी भी झंडे है

नेट             (flags & otherFlags) != 0

Enums.NET flags.HasAnyFlags(otherFlags)


झंडे जाओ

नेट

Enumerable.Range(0, 64)
  .Where(bit => ((flags.GetTypeCode() == TypeCode.UInt64 ? (long)(ulong)flags : Convert.ToInt64(flags)) & (1L << bit)) != 0)
  .Select(bit => Enum.ToObject(flags.GetType(), 1L << bit))`

Enums.NET flags.GetFlags()


मैं इन सुधारों को .NET कोर में शामिल करने की कोशिश कर रहा हूँ और शायद पूर्ण .NET फ्रेमवर्क। आप यहां मेरे प्रस्ताव की जांच कर सकते हैं


7

C ++ सिंटेक्स, बिट 0 मानकर एलएसबी है, यह मानते हुए कि ध्वज लंबे हैं:

जाँच करें कि क्या सेट है:

flags & (1UL << (bit to test# - 1))

अगर सेट नहीं है तो जांचें:

invert test !(flag & (...))

सेट:

flag |= (1UL << (bit to set# - 1))

स्पष्ट:

flag &= ~(1UL << (bit to clear# - 1))

टॉगल:

flag ^= (1UL << (bit to set# - 1))

3

सर्वश्रेष्ठ प्रदर्शन और शून्य कचरा के लिए, इसका उपयोग करें:

using System;
using T = MyNamespace.MyFlags;

namespace MyNamespace
{
    [Flags]
    public enum MyFlags
    {
        None = 0,
        Flag1 = 1,
        Flag2 = 2
    }

    static class MyFlagsEx
    {
        public static bool Has(this T type, T value)
        {
            return (type & value) == value;
        }

        public static bool Is(this T type, T value)
        {
            return type == value;
        }

        public static T Add(this T type, T value)
        {
            return type | value;
        }

        public static T Remove(this T type, T value)
        {
            return type & ~value;
        }
    }
}

2

थोड़ा सा परीक्षण करने के लिए आप निम्नलिखित कार्य करेंगे: (झंडे को 32 बिट संख्या मानकर)

टेस्ट बिट:

if((flags & 0x08) == 0x08)
(यदि बिट 4 सेट है तो इसका सही) टॉगल बैक (1 - 0 या 0 - 1):
flags = flags ^ 0x08;
बिट 4 को शून्य में रीसेट करें:
flags = flags & 0xFFFFFF7F;


2
-1 चूंकि यह भी एनम के साथ परेशान नहीं करता है? इसके अलावा, मूल्यों को हाथ से कोडित करना नाजुक है ... मैं कम से कम ~0x08इसके बजाय लिखूंगा 0xFFFFFFF7... (0x8 के लिए वास्तविक मुखौटा)
बेन मोशर

1
पहले मैं सोच रहा था कि बेन -1 कठोर था, लेकिन "0xFFFFFF7F" का उपयोग इसे विशेष रूप से खराब उदाहरण बनाता है।
टूलमेकरसैट

2

यह डेल्फी में इंडेक्सर्स के रूप में सेट का उपयोग करके प्रेरित किया गया था, जब वापस:

/// Example of using a Boolean indexed property
/// to manipulate a [Flags] enum:

public class BindingFlagsIndexer
{
  BindingFlags flags = BindingFlags.Default;

  public BindingFlagsIndexer()
  {
  }

  public BindingFlagsIndexer( BindingFlags value )
  {
     this.flags = value;
  }

  public bool this[BindingFlags index]
  {
    get
    {
      return (this.flags & index) == index;
    }
    set( bool value )
    {
      if( value )
        this.flags |= index;
      else
        this.flags &= ~index;
    }
  }

  public BindingFlags Value 
  {
    get
    { 
      return flags;
    } 
    set( BindingFlags value ) 
    {
      this.flags = value;
    }
  }

  public static implicit operator BindingFlags( BindingFlagsIndexer src )
  {
     return src != null ? src.Value : BindingFlags.Default;
  }

  public static implicit operator BindingFlagsIndexer( BindingFlags src )
  {
     return new BindingFlagsIndexer( src );
  }

}

public static class Class1
{
  public static void Example()
  {
    BindingFlagsIndexer myFlags = new BindingFlagsIndexer();

    // Sets the flag(s) passed as the indexer:

    myFlags[BindingFlags.ExactBinding] = true;

    // Indexer can specify multiple flags at once:

    myFlags[BindingFlags.Instance | BindingFlags.Static] = true;

    // Get boolean indicating if specified flag(s) are set:

    bool flatten = myFlags[BindingFlags.FlattenHierarchy];

    // use | to test if multiple flags are set:

    bool isProtected = ! myFlags[BindingFlags.Public | BindingFlags.NonPublic];

  }
}

2
यहां तक ​​कि अगर बाइंडफ्लैग्स बाइट एनम है: यह भी संकलित नहीं करता है: यह.फ्लैग & = ~ सूचकांक;
अमूलर

0

C ++ ऑपरेशन हैं: & | ^ ~ (के लिए और, या, xor और बिटवाइज़ ऑपरेशंस)। ब्याज की भी >> और << हैं, जो बिटशिफ्ट ऑपरेशन हैं।

इसलिए, किसी झंडे में सेट होने के लिए परीक्षण करने के लिए, आप उपयोग करेंगे: यदि (झंडे और 8) // परीक्षण बिट 4 सेट किया गया है


8
प्रश्न सी # से संबंधित है, न कि सी ++
एंडी जॉनसन

3
दूसरी ओर, C # समान ऑपरेटरों का उपयोग करता है: msdn.microsoft.com/en-us/library/6a71f45d.aspx
ToolmakerSteve

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