कैसे सी # में झंडे की तुलना करने के लिए?


155

मेरे पास एक झंडा है।

[Flags]
public enum FlagTest
{
    None = 0x0,
    Flag1 = 0x1,
    Flag2 = 0x2,
    Flag3 = 0x4
}

यदि कथन सत्य का मूल्यांकन करता है तो मैं नहीं कर सकता।

FlagTest testItem = FlagTest.Flag1 | FlagTest.Flag2;

if (testItem == FlagTest.Flag1)
{
    // Do something,
    // however This is never true.
}

मैं इसे कैसे सच कर सकता हूं?


अगर मैं गलत हूं तो मुझे सुधारें, क्या ध्वज मूल्य के रूप में इस्तेमाल किया जाना उचित है?
रॉय ली

4
@Roylee: 0 स्वीकार्य है, और कोई भी झंडे सेट का परीक्षण करने के लिए "कोई नहीं" या "अपरिभाषित" ध्वज रखना एक अच्छा विचार है। यह किसी भी तरह से आवश्यक नहीं है, लेकिन यह एक अच्छा अभ्यास है। इस बारे में याद रखने वाली महत्वपूर्ण बात लियोनिद ने अपने जवाब में बताई है।
एंडी

5
@Roylee यह वास्तव Noneमें शून्य के मूल्य के साथ एक ध्वज प्रदान करने के लिए Microsoft द्वारा अनुशंसित है । देखें msdn.microsoft.com/en-us/library/vstudio/…
ThatMatthew

बहुत से लोग यह भी तर्क देते हैं कि बिट तुलना करना बहुत मुश्किल है, इसलिए झंडे के संग्रह के पक्ष में से बचा जाना चाहिए, जहाँ आप सिर्फ संग्रह कर सकते हैं। झंडा
माइक

आप काफी करीब छोड़कर आप आप तर्क को उलटने के लिए है, तो आप बिटवाइज़ की जरूरत थे, &तुलना के लिए ऑपरेटर, |एक अतिरिक्त की तरह है: 1|2=3, 5|2=7, 3&2=2, 7&2=2, 8&2=00का मूल्यांकन करता है false, बाकी सब कुछ करने के लिए true
दामियन वोगेल

जवाबों:


321

.NET 4 में एक नया तरीका Enum.HasFlag है । यह आपको लिखने की अनुमति देता है:

if ( testItem.HasFlag( FlagTest.Flag1 ) )
{
    // Do Stuff
}

जो बहुत अधिक पठनीय है, IMO।

.NET स्रोत इंगित करता है कि यह स्वीकृत उत्तर के समान तर्क करता है:

public Boolean HasFlag(Enum flag) {
    if (!this.GetType().IsEquivalentTo(flag.GetType())) {
        throw new ArgumentException(
            Environment.GetResourceString(
                "Argument_EnumTypeDoesNotMatch", 
                flag.GetType(), 
                this.GetType()));
    }

    ulong uFlag = ToUInt64(flag.GetValue()); 
    ulong uThis = ToUInt64(GetValue());
    // test predicate
    return ((uThis & uFlag) == uFlag); 
}

23
आह, आखिरकार कुछ आउट ऑफ द बॉक्स। यह बहुत अच्छा है, मैं लंबे समय से इस अपेक्षाकृत सरल सुविधा की प्रतीक्षा कर रहा हूं। खुशी है कि उन्होंने इसमें फिसलने का फैसला किया।
रॉब वैन ग्रोन्यूवाड

9
ध्यान दें, हालांकि, इस विधि के साथ प्रदर्शन के मुद्दों को दिखाने के नीचे दिए गए उत्तर - यह कुछ लोगों के लिए एक मुद्दा हो सकता है। खुशी से मेरे लिए नहीं।
एंडी मोर्टिमर 10

2
इस विधि के लिए प्रदर्शन पर विचार बॉक्सिंग है क्योंकि यह Enumवर्ग के एक उदाहरण के रूप में तर्क लेता है ।
एडम हल्ड्सवर्थ

1
प्रदर्शन के मुद्दे पर सुझाव के लिए, इस उत्तर को देखें: stackoverflow.com/q/7368652/200443
Maxence

180
if ((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
     // Do something
}

(testItem & FlagTest.Flag1) एक बिटवाइज़ और ऑपरेशन है।

FlagTest.Flag1001ओपी की दुश्मनी के बराबर है । अब कहते हैं testItemकि फ्लैग 1 और फ्लैग 2 (तो यह बिटवाइस है 101):

  001
 &101
 ----
  001 == FlagTest.Flag1

2
वास्तव में यहाँ तर्क क्या है? विधेय को इस तरह क्यों लिखना पड़ता है?
इयान आर। ओ'ब्रायन

4
@ IanR.O'Brien Flag1 | फ्लैग 2 का अनुवाद 001 या 010 है, जो 011 के समान है, अब यदि आप उस 011 == फ्लैग 1 की समानता करते हैं या 011 == 001 का अनुवाद करते हैं, तो यह हमेशा गलत साबित होता है। अब अगर आप एक बिट वाइज और फ्लैग 1 के साथ करते हैं तो यह 011 और 001 में बदल जाता है जो 001 देता है जो अब समानता को पूरा करता है, क्योंकि यह सच है क्योंकि 001 == 001
pqsk

यह सबसे अच्छा समाधान है क्योंकि HasFlags अधिक संसाधन-गहन है। और इसके अलावा हस्फ्लैग क्या कर रहा है, इसके बारे में सब कुछ कंपाइलर द्वारा किया गया है
सेबेस्टियन ज़ावरी वाईनोनीकी

78

जिन लोगों को यह समझने में परेशानी होती है कि स्वीकृत समाधान के साथ क्या हो रहा है (जो यह है),

if ((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
    // Do stuff.
}

testItem (प्रश्न के अनुसार) के रूप में परिभाषित किया गया है,

testItem 
 = flag1 | flag2  
 = 001 | 010  
 = 011

फिर, यदि कथन में, तुलना के बाएँ हाथ है,

(testItem & flag1) 
 = (011 & 001) 
 = 001

और पूर्ण यदि कथन (जो कि यदि flag1सेट किया गया है तो सत्य का मूल्यांकन करता है testItem),

(testItem & flag1) == flag1
 = (001) == 001
 = true

25

@ फिल-Devaney

ध्यान दें कि सरलतम मामलों को छोड़कर, 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 ms की तुलना में, HasFlags विस्तार विधि में 4793 एमएस होता है।


5
वास्तव में। यदि आप HasFlag के कार्यान्वयन को देखते हैं, तो आप देखेंगे कि यह दोनों ऑपरेंड पर एक "गेटटाइप ()" करता है, जो काफी धीमा है। फिर यह "Enum.ToUInt64 (value.GetValue ()) करता है;" बिटकॉइन चेक करने से पहले दोनों ऑपरेंड पर।
user276648

1
मैंने कई बार आपका परीक्षण किया और हस्फ्लैग्स के लिए ~ 500ms और बिटवाइज़ के लिए ~ 32ms प्राप्त किए। हालांकि अभी भी बिटवाइस के साथ तेजी से परिमाण का एक क्रम, हैसफ्लैग्स आपके परीक्षण से नीचे परिमाण का एक आदेश था। (एक 2.5GHz Core i3 और .NET 4.5 पर परीक्षण
दौड़ाया

1
@MarioVW .NET 4 पर कई बार चल रहा है, i7-3770 ~ 2400ms ~ ~ 20ms AnyCPU (64-बिट) मोड पर, और ~ 3000ms बनाम ~ 20ms 32-बिट मोड पर देता है। .NET 4.5 ने इसे थोड़ा अनुकूलित किया है। 64-बिट और 32-बिट बिल्ड के बीच प्रदर्शन अंतर पर भी ध्यान दें, जो तेजी से 64-बिट अंकगणितीय (पहली टिप्पणी देखें) के कारण हो सकता है।
बॉब

1
(«flag var» & «flag value») != 0मेरे लिए काम नहीं करता है। स्थिति हमेशा विफल रहती है और मेरे संकलक (यूनिटी 3 डी के मोनो 2.6.5) ने "चेतावनी CS0162: अप्राप्य कोड का पता लगाया" रिपोर्ट करता है जब एक में उपयोग किया जाता है if (…)
स्लिप डी। थॉम्पसन

1
@ wraith808: मुझे एहसास हुआ कि गलती मेरे परीक्षण के साथ कर रही थी कि आपके पास सही है - … = 1, … = 2, … = 4उपयोग करते समय एनम मूल्यों पर 2 की शक्ति गंभीर रूप से महत्वपूर्ण है [Flags]। मैं यह मान रहा था कि यह 1Po2 द्वारा स्वचालित रूप से प्रविष्टियों को शुरू और आगे बढ़ाएगा। व्यवहार MS .NET और यूनिटी की दिनांकित मोनो में लगातार दिखता है। मेरी क्षमायाचना इस पर डाल रही है।
स्लिप डी। थॉम्पसन

21

मैंने इसे करने के लिए एक विस्तार विधि की स्थापना की: संबंधित प्रश्न

मूल रूप से:

public static bool IsSet( this Enum input, Enum matchTo )
{
    return ( Convert.ToUInt32( input ) & Convert.ToUInt32( matchTo ) ) != 0;
}

तो आप कर सकते हैं:

FlagTests testItem = FlagTests.Flag1 | FlagTests.Flag2;

if( testItem.IsSet ( FlagTests.Flag1 ) )
    //Flag1 is set

संयोग से मैं जिस शत्रु का उपयोग करता हूं, वह झंडे के लिए मानक, बहुवचन के लिए एकवचन है। इस तरह आप एनम नाम से जानते हैं कि क्या यह कई मान रख सकता है।


यह एक टिप्पणी होनी चाहिए, लेकिन जैसा कि मैं एक नया उपयोगकर्ता हूं, ऐसा लगता है कि मैं अभी तक टिप्पणियां नहीं जोड़ सकता हूं ... सार्वजनिक स्थैतिक बूल IsSet (यह Enum इनपुट, Enum matchTo) {return (Convert.ToUInt32 (इनपुट) और कनवर्ट करें .ToUInt32 (माचिस))! = 0; } क्या किसी भी तरह के एनम के साथ संगत होने का एक तरीका है (क्योंकि यहां काम नहीं करेगा यदि आपकी एनम टाइप UInt64 की है या नकारात्मक मान हो सकती है)?
user276648 3

यह Enum.HasFlag (Enum) (.net 4.0 में उपलब्ध) के साथ काफी बेमानी है
PPC

1
@PPC मैं बिल्कुल बेमानी नहीं कहूंगा - बहुत सारे लोग ढांचे के पुराने संस्करणों पर विकसित हो रहे हैं। आप सही हैं, लेकिन। नेट 4 उपयोगकर्ताओं को इसका उपयोग करना चाहिएHasFlag इसके बजाय एक्सटेंशन का ।
कीथ

4
@ कीथ: इसके अलावा, एक उल्लेखनीय अंतर है: ((फ्लैगटेस्ट) 0x1) ।हसफ्लैग (0x0) सही लौटेगा, जो एक व्यवहार नहीं हो सकता है या नहीं
PPC

19

सलाह का एक और टुकड़ा ... ध्वज के साथ मानक बाइनरी चेक कभी न करें जिसका मूल्य "0" है। इस ध्वज पर आपकी जाँच हमेशा सत्य होगी।

[Flags]
public enum LevelOfDetail
{
    [EnumMember(Value = "FullInfo")]
    FullInfo=0,
    [EnumMember(Value = "BusinessData")]
    BusinessData=1
}

यदि आप FullInfo के खिलाफ इनपुट पैरामीटर को बाइनरी चेक करते हैं - तो आपको मिलता है:

detailLevel = LevelOfDetail.BusinessData;
bool bPRez = (detailLevel & LevelOfDetail.FullInfo) == LevelOfDetail.FullInfo;

bPRez हमेशा कुछ भी सच होगा और हमेशा 0 == 0 होगा।


इसके बजाय आपको बस जांचना चाहिए कि इनपुट का मान 0 है:

bool bPRez = (detailLevel == LevelOfDetail.FullInfo);

मैंने ऐसे ही 0-फ्लैग बग को ठीक किया। मुझे लगता है कि यह .NET फ्रेमवर्क (3.5) में एक डिज़ाइन त्रुटि है क्योंकि आपको यह जानने की आवश्यकता है कि परीक्षण करने से पहले ध्वज का मान 0 में से कौन सा है।
थ्रेश


5

बिट संचालन के लिए, आपको बिटवाइज़ ऑपरेटरों का उपयोग करने की आवश्यकता है।

यह काम कर जाना चाहिए:

if ((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
    // Do something,
    // however This is never true.
}

संपादित करें: यदि मेरा चेक-फिक्स्ड है, तो मैं अपने C / C ++ तरीके से वापस फिसल गया (इसे इंगित करने के लिए रयान फ़ार्ले को धन्यवाद)


5

एडिट के बारे में। आप इसे सच नहीं कर सकते। मेरा सुझाव है कि आप अपनी आवश्यकता के सिंटैक्स के करीब पहुंचने के लिए एक और वर्ग (या विस्तार विधि) में जो चाहते हैं उसे लपेटें।

अर्थात

public class FlagTestCompare
{
    public static bool Compare(this FlagTest myFlag, FlagTest condition)
    {
         return ((myFlag & condition) == condition);
    }
}

4

इसे इस्तेमाल करे:


if ((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
    // do something
}
मूल रूप से, आपका कोड पूछ रहा है कि क्या दोनों ध्वज सेट एक ध्वज सेट के समान हैं, जो स्पष्ट रूप से गलत है। उपरोक्त कोड केवल फ्लैग 1 बिट सेट को छोड़ देगा यदि यह बिल्कुल सेट है, तो इस परिणाम की तुलना फ्लैग 1 से करें।


1

[झंडे] के बिना भी, आप कुछ इस तरह का उपयोग कर सकते हैं

if((testItem & (FlagTest.Flag1 | FlagTest.Flag2 ))!=0){
//..
}

या यदि आपके पास शून्य मान एनम है

if((testItem & (FlagTest.Flag1 | FlagTest.Flag2 ))!=FlagTest.None){
//..
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.