সি # তে পতাকা কীভাবে তুলনা করবেন?


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

আমি কীভাবে এটি সত্য করতে পারি?


আমি ভুল হলে আমাকে সংশোধন করুন, পতাকাটির মান হিসাবে ব্যবহার করা কি 0 উপযুক্ত?
রায় লি

4
@ রয়লি: ০ টি গ্রহণযোগ্য এবং কোনও পতাকা নেই বলে পরীক্ষা করার জন্য একটি "কিছুই নয়" বা "অপরিজ্ঞাত" পতাকা রাখা ভাল ধারণা। এটি কোনওভাবেই প্রয়োজন হয় না তবে এটি একটি ভাল অনুশীলন। এটির জন্য গুরুত্বপূর্ণ বিষয়টি লিওনিড তার উত্তরে উল্লেখ করেছেন।
অ্যান্ডি

5
@ রোলি আসলে মাইক্রোসফ্ট Noneশূন্যের মান সহ একটি পতাকা সরবরাহ করার পরামর্শ দিচ্ছে । দেখুন msdn.microsoft.com/en-us/library/vstudio/...
ThatMatthew

মানুষ অনেক এছাড়াও তর্ক বিট তুলনা তাই পতাকার একটি সংগ্রহ, যেখানে আপনি শুধু collection.contains পতাকা কি করতে পারেন পক্ষে এড়িয়ে চলা উচিত পড়তে খুব কঠিন
MikeT

আপনি বেশ কাছে ব্যতীত আপনি যুক্তিবিজ্ঞান উল্টানো আছে, তবে আপনি, bitwise প্রয়োজন ছিল, &তুলনা জন্য অপারেটর, |একটি ছাড়াও মত হল: 1|2=3, 5|2=7, 3&2=2, 7&2=2, 8&2=0। অন্য সব কিছুর কাছে 0মূল্যায়ন করে । falsetrue
দামিয়ান ভোগেল

উত্তর:


321

.NET 4 এ একটি নতুন পদ্ধতি Enum.HasFlag রয়েছে । এটি আপনাকে লিখতে দেয়:

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

যা অনেক বেশি পাঠযোগ্য, আইএমও।

.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
আহ, অবশেষে বাক্সের বাইরে কিছু। এটি দুর্দান্ত, আমি দীর্ঘদিন ধরে এই তুলনামূলক সহজ বৈশিষ্ট্যের জন্য অপেক্ষা করছিলাম। খুশি যে তারা এটা চিলতা করার সিদ্ধান্ত নিয়েছে।
রব ভ্যান Groenewoud

9
দ্রষ্টব্য, তবে নীচের উত্তরগুলি এই পদ্ধতিতে পারফরম্যান্সের সমস্যাগুলি দেখায় - এটি কিছু লোকের পক্ষে সমস্যা হতে পারে। সুখ আমার জন্য না।
অ্যান্ডি মর্টিমার

2
এই পদ্ধতির জন্য পারফরম্যান্স বিবেচনা বক্সিং হয় কারণ এটি Enumশ্রেণীর উদাহরণ হিসাবে আর্গুমেন্ট নেয় ।
অ্যাডাম হল্ডসওয়ার্থ

1
পারফরম্যান্স ইস্যুতে তথ্যের জন্য, এই উত্তরটি দেখুন: stackoverflow.com/q/7368652/200443
ম্যাক্সেন্স

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
@ আইয়ানআর.ও'ব্রায়ান ফ্ল্যাগ 1 | পতাকা 2 টি 001 বা 010 তে অনুবাদ করে যা 011 এর সমান, এখন আপনি যদি 011 == পতাকা 1 বা অনুবাদ করা 011 == 001 এর সমতা করেন তবে এটি সর্বদা মিথ্যা প্রত্যাবর্তন করে। এখন আপনি যদি ফ্ল্যাজ 1 দিয়ে কিছুটা দিক ও করে থাকেন তবে এটি 011 এবং 001 এ অনুবাদ করে যা এখন 001
ফেরায় সমতাটি

এটি সেরা সমাধান কারণ হাসফ্লেগগুলি অনেক বেশি সংস্থান-নিবিড়। এবং তদ্ব্যতীত, হাসফ্ল্যাগগুলি যা করছে তা সবকিছুই এখানে কম্পাইলার দ্বারা সম্পন্ন করা হয়েছে
সেবাস্তিয়ান জাওয়ারি উইনিওইকিকি

78

যাঁরা গ্রহণযোগ্য সমাধান (যা এটি) এর সাথে কী ঘটছে তা কল্পনা করতে সমস্যা হয়,

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

testItem (প্রশ্ন অনুযায়ী) হিসাবে সংজ্ঞায়িত করা হয়,

testItem 
 = flag1 | flag2  
 = 001 | 010  
 = 011

তারপরে, যদি বিবৃতিতে তুলনাটির বাম দিকটি হয়,

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

এবং সম্পূর্ণ if স্টেটমেন্ট (এটি যদি flag1সেট করা থাকে তবে এটি সত্যে মূল্যায়ন করে testItem),

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

25

@ ফিল-devaney

মনে রাখবেন যে সহজতম ক্ষেত্রে ব্যতীত এনাম.হ্যাসফ্ল্যাগ ম্যানুয়ালি কোড লেখার তুলনায় ভারী পারফরম্যান্সের জরিমানা বহন করে। নিম্নলিখিত কোড বিবেচনা করুন:

[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 এমএসের তুলনায়, হ্যাশফ্লাগস এক্সটেনশন পদ্ধতিটি একটি তীব্র 4793 এমএস নেয়।


5
প্রকৃতপক্ষে. আপনি যদি হ্যাশফ্লাগের বাস্তবায়নের দিকে নজর দেন তবে আপনি দেখতে পাবেন যে এটি উভয় অপারেণ্ডে একটি "গেটটাইপ ()" করে যা বেশ ধীর। তারপরে এটি "Enum.ToUInt64 (value.GetValue ());" বিটওয়াইজ চেক করার আগে উভয় অপারেন্ডে।
ব্যবহারকারী 276648

1
আমি আপনার পরীক্ষাটি বেশ কয়েকবার চালিয়েছি এবং হাসফ্ল্যাগের জন্য ms 500ms এবং বিটওয়াইজের জন্য ms 32ms পেয়েছি। বিটওয়াসাসহ তত দ্রুততর আকারের ক্রম থাকা অবস্থায়, হাসফ্ল্যাজগুলি আপনার পরীক্ষা থেকে নিচে মাত্রার ক্রম ছিল। (একটি 2.5GHz কোর i3 এবং .NET 4.5 এ পরীক্ষা
চালিয়েছেন

1
@ মারিওভিডাব্লু। নেট 4, i7-3770 এ কয়েকবার চলমান CP 2400ms বনাম CP 20ms যেকোন সিপিইউ (64-বিট) মোডে এবং 32-বিট মোডে ms 3000ms বনাম ms 20 মিমি দেয়। .NET 4.5 এটিকে কিছুটা অপ্টিমাইজ করেছে। এছাড়াও 64-বিট এবং 32-বিট বিল্ডগুলির মধ্যে পারফরম্যান্স পার্থক্যটি নোট করুন, যা দ্রুত -৪-বিট পাটিগণিতের কারণে হতে পারে (প্রথম মন্তব্যটি দেখুন)।
বব

1
(«flag var» & «flag value») != 0আমার জন্য কাজ করে না। শর্ত সবসময় ব্যর্থ হয় এবং আমার কম্পাইলার (Unity3D এর মনো 2.6.5) রিপোর্ট "সাবধান CS0162: অনধিগম্য কোড সনাক্ত করা" যখন একটি ব্যবহৃত if (…)
স্লিপ ডি থম্পসন

1
@ রথ 808: আমি বুঝতে পেরেছিলাম যে ভুলটি আমার পরীক্ষা দিয়েছিল যে আপনি নিজের মধ্যে সঠিক করেছেন - এনাম মানগুলিতে 2 পাওয়ারটি … = 1, … = 2, … = 4ব্যবহারের সময় সমালোচনামূলকভাবে গুরুত্বপূর্ণ [Flags]। আমি ধরে 1নিচ্ছিলাম যে এটি Po2s দ্বারা এন্ট্রিগুলি শুরু করবে এবং স্বয়ংক্রিয়ভাবে পোস্ট হবে। আচরণ এমএস। নেট এবং ইউনিটির তারিখের মনোতে জুড়ে সুসংগত দেখায়। আমার ক্ষমাপ্রার্থী এটি আপনার উপর চাপিয়ে দিচ্ছে।
স্লিপ ডি থম্পসন

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

ঘটনাক্রমে আমি কনভেনশনটি এনামগুলিতে ব্যবহার করি এটি স্ট্যান্ডার্ডের জন্য একক, পতাকাগুলির জন্য বহুবচন। এইভাবে আপনি এনাম নাম থেকে জানেন যে এটি একাধিক মান ধরে রাখতে পারে কিনা।


এটি একটি মন্তব্য হওয়া উচিত তবে আমি একজন নতুন ব্যবহারকারী হিসাবে দেখে মনে হচ্ছে আমি এখনও মন্তব্য যুক্ত করতে পারছি না ... পাবলিক স্ট্যাটিক বুল ইসসেট (এই এনুম ইনপুট, এনাম ম্যাচটো) {প্রত্যাবর্তন (কনভার্ট.ট্যুআইএনটি 32 (ইনপুট)) এবং রূপান্তর .ToUInt32 (ম্যাচটো))! = 0; Any কোনও এনামের সাথে কি সামঞ্জস্যপূর্ণ হওয়ার কোনও উপায় আছে (কারণ এখানে যদি আপনার এনামটি ইউআইএনটি 64৪ টাইপের হয় বা নেতিবাচক মান থাকতে পারে) তবে এটি কাজ করবে না?
ব্যবহারকারী 276648

এটি Enum.HasFlag (Enum) (.NET 4.0 এ উপলভ্য)
পিপিসি

1
@ পিপিসি আমি একেবারে অপ্রয়োজনীয় বলব না - কাঠামোর পুরানো সংস্করণে প্রচুর লোক বিকাশ করছে। যদিও আপনি ঠিক বলেছেন, নেট 4 ব্যবহারকারীদের HasFlagপরিবর্তে এক্সটেনশনটি ব্যবহার করা উচিত ।
কিথ

4
@Keith: এছাড়াও, একটি উল্লেখযোগ্য পার্থক্য নেই: ((FlagTest) 0x1) .HasFlag (0x0), সত্য ফিরে আসবে যা পারে অথবা একটি চেয়েছিলেন আচরণ নাও হতে পারে
পিপিসি

19

পরামর্শের আরও একটি অংশ ... পতাকাটির সাথে কখনও মানক বাইনারি চেক করবেন না যার মান "0"। এই পতাকাটিতে আপনার চেক সর্বদা সত্য হবে।

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

আপনি যদি ফুলইনফোর বিপরীতে ইনপুট প্যারামিটার বাইনারি করেন - আপনি পাবেন:

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

বিপিআরজেজ সর্বদা যে কোনও কিছুই & 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.
}

সম্পাদনা করুন: আমার চেকটি ঠিক করে ফেললে - আমি আমার সি / সি ++ উপায়ে পিছিয়ে গেলাম (এটি দেখানোর জন্য রায়ান ফারলেকে ধন্যবাদ)


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){
//..
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.