C # में बिटमास्क का उपयोग करना


97

मान लीजिए कि मेरे पास निम्नलिखित हैं

int susan = 2; //0010
int bob = 4; //0100
int karen = 8; //1000

और मैं एक विधि के पैरामीटर के रूप में 10 (8 + 2) पास करता हूं और मैं इसे सूसन और करेन का मतलब देना चाहता हूं।

मुझे पता है कि 10 1010 है

लेकिन मैं यह देखने के लिए कुछ तर्क कैसे कर सकता हूं कि क्या एक विशिष्ट बिट की जाँच की जाती है

if (condition_for_karen) // How to quickly check whether effective karen bit is 1

अभी मैं सभी के बारे में सोच सकता हूं कि क्या मैं पास किया गया नंबर है या नहीं

14 // 1110
12 // 1100
10 // 1010
8 //  1000

जब मेरे वास्तविक विश्व परिदृश्य में बड़ी संख्या में वास्तविक बिट्स होते हैं, तो यह अव्यवहारिक लगता है, सिर्फ एक जांच का उपयोग करके बेहतर तरीका है कि क्या मैं सिर्फ करेन के लिए शर्त को पूरा करूं या नहीं?

मैं बाईं ओर शिफ्ट करने के बारे में सोच सकता हूं, फिर वापस दाईं ओर शिफ्टिंग कर सकता हूं।


7
बस उपयोग पर टिप्पणी करना था। यदि आप बिट संचालन कर रहे हैं, तो आपको केवल बिट मैनिपुलेटिंग ऑपरेटरों का उपयोग करना चाहिए। अर्थात, इसे (8 | 2) समझिए, (8 + 2) नहीं।
जेफ मर्काडो

करेन भी तुरंत अपने प्रबंधक से बात करना चाहेंगे।
क्रिएथिक

जवाबों:


198

यह करने के लिए परंपरागत तरीके से उपयोग करने के लिए है Flagsएक पर विशेषता enum:

[Flags]
public enum Names
{
    None = 0,
    Susan = 1,
    Bob = 2,
    Karen = 4
}

फिर आप एक विशेष नाम के लिए जाँच करेंगे:

Names names = Names.Susan | Names.Bob;

// evaluates to true
bool susanIsIncluded = (names & Names.Susan) != Names.None;

// evaluates to false
bool karenIsIncluded = (names & Names.Karen) != Names.None;

लॉजिकल बिटवाइज़ कॉम्बिनेशन को याद रखना कठिन हो सकता है, इसलिए मैं अपने आप को एक FlagsHelperकक्षा * के साथ जीवन को आसान बनाता हूं :

// The casts to object in the below code are an unfortunate necessity due to
// C#'s restriction against a where T : Enum constraint. (There are ways around
// this, but they're outside the scope of this simple illustration.)
public static class FlagsHelper
{
    public static bool IsSet<T>(T flags, T flag) where T : struct
    {
        int flagsValue = (int)(object)flags;
        int flagValue = (int)(object)flag;

        return (flagsValue & flagValue) != 0;
    }

    public static void Set<T>(ref T flags, T flag) where T : struct
    {
        int flagsValue = (int)(object)flags;
        int flagValue = (int)(object)flag;

        flags = (T)(object)(flagsValue | flagValue);
    }

    public static void Unset<T>(ref T flags, T flag) where T : struct
    {
        int flagsValue = (int)(object)flags;
        int flagValue = (int)(object)flag;

        flags = (T)(object)(flagsValue & (~flagValue));
    }
}

यह मुझे उपरोक्त कोड को फिर से लिखने की अनुमति देगा:

Names names = Names.Susan | Names.Bob;

bool susanIsIncluded = FlagsHelper.IsSet(names, Names.Susan);

bool karenIsIncluded = FlagsHelper.IsSet(names, Names.Karen);

नोट मैं Karenऐसा करके सेट में भी शामिल हो सकता हूं :

FlagsHelper.Set(ref names, Names.Karen);

और मैं Susanएक समान तरीके से निकाल सकता था :

FlagsHelper.Unset(ref names, Names.Susan);

* जैसा कि पोरगेस ने बताया, IsSetऊपर दी गई विधि का एक समकक्ष .NET 4.0 में पहले से मौजूद है Enum.HasFlagSetऔर Unsetतरीकों, हालांकि समकक्ष है प्रतीत नहीं होते हैं; इसलिए मैं अभी भी कहूंगा कि इस वर्ग में कुछ योग्यता है।


नोट: एनम का उपयोग करना इस समस्या से निपटने का पारंपरिक तरीका है। आप इसके बजाय इनट्स का उपयोग करने के लिए उपरोक्त सभी कोड का पूरी तरह से अनुवाद कर सकते हैं और यह बस के रूप में भी काम करेगा।


14
वास्तव में काम करने वाला पहला कोड होने के लिए +1। आप भी कर सकते हैं (names & Names.Susan) == Names.Susan, जिसकी आवश्यकता नहीं है None
मैथ्यू फ्लेशेन

1
@ मैथ्यू: ओह, हाँ, अच्छी बात है। मुझे लगता है कि मुझे बस अपने Noneसभी enums के लिए एक मूल्य को परिभाषित करने की आदत है , क्योंकि मुझे लगता है कि यह कई परिदृश्यों में सुविधाजनक होने के लिए समाप्त होता है।
दान ताओ

30
इस में बनाया गया है, आप अपने सहायक तरीकों की जरूरत नहीं है ...var susanIsIncluded = names.HasFlag(Names.Susan);
porges

2
@Porges: वाह, पता नहीं मैं कैसे चूक गया ... कि बाहर इशारा करने के लिए धन्यवाद! (ऐसा लगता है कि यह केवल .NET 4.0 के रूप में उपलब्ध है, हालांकि ... भी, Setविधि के लिए कोई समकक्ष नहीं है । इसलिए, मैं कहूंगा कि सहायक विधियां कम से कम पूरी तरह से बेकार नहीं हैं ।)
दान ताओ

6
ध्यान दें, कि उपयोग करना names.HasFlag(Names.Susan)ऐसा है (names & Names.Susan) == Names.Susanजो हमेशा पसंद नहीं होता है (names & Names.Susan) != Names.None। उदाहरण के लिए यदि आप names.HasFlag(Names.none)names.HasFlag(Names.Susan|Names.Karen)
जाँचेंगे

20
if ( ( param & karen ) == karen )
{
  // Do stuff
}

बिटेन 'और' करेन को छोड़कर हर चीज को बाहर निकाल देगा, जो करेन का प्रतिनिधित्व करता है। जब तक प्रत्येक व्यक्ति को एक बिट स्थिति से दर्शाया जाता है, आप एक साधारण से कई लोगों की जांच कर सकते हैं:

if ( ( param & karen ) == karen )
{
  // Do Karen's stuff
}
if ( ( param & bob ) == bob )
  // Do Bob's stuff
}

12

मैंने यहां एक उदाहरण शामिल किया है जो दर्शाता है कि आप एक डेटाबेस कॉलम में एक इंट के रूप में मास्क को कैसे स्टोर कर सकते हैं, और आप बाद में मुखौटा को कैसे बहाल करेंगे:

public enum DaysBitMask { Mon=0, Tues=1, Wed=2, Thu = 4, Fri = 8, Sat = 16, Sun = 32 }


DaysBitMask mask = DaysBitMask.Sat | DaysBitMask.Thu;
bool test;
if ((mask & DaysBitMask.Sat) == DaysBitMask.Sat)
    test = true;
if ((mask & DaysBitMask.Thu) == DaysBitMask.Thu)
    test = true;
if ((mask & DaysBitMask.Wed) != DaysBitMask.Wed)
    test = true;

// Store the value
int storedVal = (int)mask;

// Reinstate the mask and re-test
DaysBitMask reHydratedMask = (DaysBitMask)storedVal;

if ((reHydratedMask & DaysBitMask.Sat) == DaysBitMask.Sat)
    test = true;
if ((reHydratedMask & DaysBitMask.Thu) == DaysBitMask.Thu)
    test = true;
if ((reHydratedMask & DaysBitMask.Wed) != DaysBitMask.Wed)
    test = true;

मैंने कुछ ऐसा ही किया, लेकिन जब मैंने मास्क को परिभाषित किया तो मैंने Mon = Math.Power (2, 0), Mang = Math.Pow (2, 1), Wed = Math.Pow (2, 2), इत्यादि का स्थान लिया। उन उपयोगों के लिए थोड़ा अधिक स्पष्ट है जो दशमलव रूपांतरण में बाइनरी के लिए उपयोग नहीं किए जाते हैं। ब्लाइंड्स बहुत अच्छा है क्योंकि यह नकाबपोश बिट को स्थानांतरित करके बूलियन परिणाम में बदल जाता है।
एनालॉग आर्सेनिस्ट

7

बिटमास्क को संयोजित करने के लिए आप बिटवाइज़- या का उपयोग करना चाहते हैं । तुच्छ मामले में जहां आपके द्वारा जोड़ा गया प्रत्येक मान बिल्कुल 1 बिट पर होता है (जैसे आपका उदाहरण), यह उन्हें जोड़ने के बराबर है। यदि आपके पास बिट्स ओवरलैपिंग है, या 'आईएनजी उन्हें मामले को इनायत से संभालती है।

एक मुखौटा के साथ आप और आपके मूल्य को डिकोड करने के लिए , जैसे:

if(val & (1<<1)) SusanIsOn();
if(val & (1<<2)) BobIsOn();
if(val & (1<<3)) KarenIsOn();

1
आप C # में बूलियन के रूप में पूर्णांक का उपयोग नहीं कर सकते।
छाया

6

आसान तरीका:

[Flags]
public enum MyFlags {
    None = 0,
    Susan = 1,
    Alice = 2,
    Bob = 4,
    Eve = 8
}

झंडे सेट करने के लिए तार्किक "या" ऑपरेटर का उपयोग करें |:

MyFlags f = new MyFlags();
f = MyFlags.Alice | MyFlags.Bob;

और यह जांचने के लिए कि क्या एक ध्वज का उपयोग शामिल है HasFlag:

if(f.HasFlag(MyFlags.Alice)) { /* true */}
if(f.HasFlag(MyFlags.Eve)) { /* false */}

यह सब जानकारी की तरह लगता है पहले से ही ऊपर प्रदान किया गया है। यदि आप कोई नई जानकारी दे रहे हैं तो आपको इसे स्पष्ट रूप से चिह्नित करना चाहिए।
sonyisda1

1
का उपयोग करने का सरल उदाहरण HasFlag()और [Flags]अन्य उत्तरों में प्रदान नहीं किया गया था।
A-Sharabiani

0

एक बिटकॉम्ब बनाम व्यक्तिगत बूल का उपयोग करने का एक अन्य वास्तव में अच्छा कारण एक वेब डेवलपर के रूप में है, जब एक वेबसाइट को दूसरे से एकीकृत किया जाता है, तो हमें अक्सर क्वेरिस्ट्रिंग में पैरामीटर या झंडे भेजने की आवश्यकता होती है। जब तक आपके सभी झंडे द्विआधारी होते हैं, तब तक बिट्स के रूप में एकल मान का उपयोग करना बहुत आसान होता है, बूल से कई मानों को भेजें। मुझे पता है कि डेटा भेजने के लिए अन्य रास्ते हैं (GET, POST, आदि), लेकिन querystring पर एक साधारण पैरामीटर ज्यादातर समय के लिए निरर्थक वस्तुओं के लिए पर्याप्त है। एक बाहरी साइट के साथ संवाद करने के लिए एक querystring पर 128 बूल मान भेजने की कोशिश करें। यह ब्राउज़रों में url querystrings पर सीमा को आगे नहीं बढ़ाने की अतिरिक्त क्षमता भी देता है


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