Pertanyaan Apa arti dari [Flags] Enum Atribut di C #?


Dari waktu ke waktu saya melihat enum seperti berikut:

[Flags]
public enum Options 
{
    None    = 0,
    Option1 = 1,
    Option2 = 2,
    Option3 = 4,
    Option4 = 8
}

Saya tidak mengerti apa sebenarnya itu [Flags]-atribut tidak.

Ada yang punya penjelasan atau contoh bagus yang bisa mereka posting?


1123
2017-08-12 04:09


asal


Jawaban:


Atribut bendera harus digunakan kapan pun enumerable mewakili kumpulan bendera, bukan satu nilai. Koleksi semacam ini biasanya dimanipulasi menggunakan operator bitwise, misalnya:

myProperties.AllowedColors = MyColor.Red | MyColor.Green | MyColor.Blue;

Perhatikan itu [Flags] dengan sendirinya tidak mengubah ini sama sekali - Semua yang dilakukannya adalah memungkinkan representasi yang bagus oleh .ToString() metode:

enum Suits { Spades = 1, Clubs = 2, Diamonds = 4, Hearts = 8 }
[Flags] enum SuitsFlags { Spades = 1, Clubs = 2, Diamonds = 4, Hearts = 8 }

...

var str1 = (Suits.Spades | Suits.Diamonds).ToString();
           // "5"
var str2 = (SuitsFlags.Spades | SuitsFlags.Diamonds).ToString();
           // "Spades, Diamonds"

Penting juga untuk dicatat bahwa [Flags]  tidak secara otomatis membuat enum menghargai kekuatan dua. Jika Anda mengabaikan nilai numerik, enum tidak akan berfungsi seperti yang diharapkan dalam operasi bitwise, karena secara default nilai dimulai dengan 0 dan kenaikan.

Deklarasi yang salah:

[Flags]
public enum MyColors
{
    Yellow,
    Green,
    Red,
    Blue
}

Nilai-nilai, jika dinyatakan dengan cara ini, akan menjadi Kuning = 0, Hijau = 1, Merah = 2, Biru = 3. Ini akan menjadikannya tidak berguna untuk digunakan sebagai bendera.

Berikut contoh deklarasi yang benar:

[Flags]
public enum MyColors
{
    Yellow = 1,
    Green = 2,
    Red = 4,
    Blue = 8
}

Untuk mengambil nilai yang berbeda di properti Anda, seseorang dapat melakukan ini:

if((myProperties.AllowedColors & MyColor.Yellow) == MyColor.Yellow)
{
    // Yellow has been set...
}

if((myProperties.AllowedColors & MyColor.Green) == MyColor.Green)
{
    // Green has been set...
}    

atau, dalam .NET 4 dan yang lebih baru:

if (myProperties.AllowedColors.HasFlag(MyColor.Yellow))
{
    // Yellow has been set...
}

Di bawah selimut

Ini berfungsi karena sebelumnya Anda menggunakan kekuatan dua dalam enumerasi Anda. Di bawah selimut, nilai enumerasi Anda terlihat seperti ini (disajikan sebagai byte, yang memiliki 8 bit yang bisa 1 atau 0)

 Yellow: 00000001
 Green:  00000010
 Red:    00000100
 Blue:   00001000

Demikian juga, setelah Anda mengatur properti Anda DiizinkanColors ke Merah, Hijau dan Biru (yang menghargai di mana OR'ed oleh pipa |), DiizinkanColors terlihat seperti ini

myProperties.AllowedColors: 00001110

Jadi, ketika Anda mengambil nilai Anda benar-benar bitwise AND'ing nilai-nilai

myProperties.AllowedColors: 00001110
             MyColor.Green: 00000010
             -----------------------
                            00000010 // Hey, this is the same as MyColor.Green!

Nilai Tidak Ada = 0

Dan mengenai penggunaan 0 dalam enumerasi Anda, mengutip dari msdn:

[Flags]
public enum MyColors
{
    None = 0,
    ....
}

Gunakan None sebagai nama flag yang menyebutkan konstanta yang nilainya nol. Anda tidak dapat menggunakan konstanta Tidak disebutkan dalam operasi bitwise AND untuk menguji bendera karena hasilnya selalu nol. Namun, Anda dapat melakukan perbandingan yang logis, bukan bitwise, antara nilai numerik dan None enumerated constant untuk menentukan apakah setiap bit dalam nilai numerik ditetapkan.

Anda dapat menemukan info lebih lanjut tentang atribut bendera dan penggunaannya di msdn dan mendesain bendera di MSDN


1752
2017-08-12 05:10



Anda juga bisa melakukan ini

[Flags]
public enum MyEnum
{
    None   = 0,
    First  = 1 << 0,
    Second = 1 << 1,
    Third  = 1 << 2,
    Fourth = 1 << 3
}

Saya menemukan penggeser bit lebih mudah daripada mengetik 4,8,16,32 dan seterusnya. Ini tidak berdampak pada kode Anda karena semuanya dilakukan pada waktu kompilasi


670
2017-08-12 04:37



Menggabungkan jawaban https://stackoverflow.com/a/8462/1037948 (deklarasi melalui bit-shifting) dan https://stackoverflow.com/a/9117/1037948 (menggunakan kombinasi dalam deklarasi) Anda dapat menggeser bit nilai sebelumnya daripada menggunakan angka. Belum tentu merekomendasikannya, tetapi hanya menunjukkan Anda bisa.

Daripada:

[Flags]
public enum Options : byte
{
    None    = 0,
    One     = 1 << 0,   // 1
    Two     = 1 << 1,   // 2
    Three   = 1 << 2,   // 4
    Four    = 1 << 3,   // 8

    // combinations
    OneAndTwo = One | Two,
    OneTwoAndThree = One | Two | Three,
}

Anda dapat menyatakan

[Flags]
public enum Options : byte
{
    None    = 0,
    One     = 1 << 0,       // 1
    // now that value 1 is available, start shifting from there
    Two     = One << 1,     // 2
    Three   = Two << 1,     // 4
    Four    = Three << 1,   // 8

    // same combinations
    OneAndTwo = One | Two,
    OneTwoAndThree = One | Two | Three,
}

Mengkonfirmasi dengan LinqPad:

foreach(var e in Enum.GetValues(typeof(Options))) {
    string.Format("{0} = {1}", e.ToString(), (byte)e).Dump();
}

Hasil dalam:

None = 0
One = 1
Two = 2
OneAndTwo = 3
Three = 4
OneTwoAndThree = 7
Four = 8

86
2018-05-16 15:03



Silakan lihat yang berikut untuk contoh yang menunjukkan deklarasi dan penggunaan potensial:

namespace Flags
{
    class Program
    {
        [Flags]
        public enum MyFlags : short
        {
            Foo = 0x1,
            Bar = 0x2,
            Baz = 0x4
        }

        static void Main(string[] args)
        {
            MyFlags fooBar = MyFlags.Foo | MyFlags.Bar;

            if ((fooBar & MyFlags.Foo) == MyFlags.Foo)
            {
                Console.WriteLine("Item has Foo flag set");
            }
        }
    }
}

44
2017-08-12 04:32



saya tanya baru-baru ini tentang sesuatu yang serupa.

Jika Anda menggunakan bendera Anda dapat menambahkan metode perluasan ke enum untuk membuat pemeriksaan flag yang ada lebih mudah (lihat posting untuk detail)

Ini memungkinkan Anda melakukan:

[Flags]
public enum PossibleOptions : byte
{
    None = 0,
    OptionOne = 1,
    OptionTwo = 2,
    OptionThree = 4,
    OptionFour = 8,

    //combinations can be in the enum too
    OptionOneAndTwo = OptionOne | OptionTwo,
    OptionOneTwoAndThree = OptionOne | OptionTwo | OptionThree,
    ...
}

Maka Anda bisa melakukan:

PossibleOptions opt = PossibleOptions.OptionOneTwoAndThree 

if( opt.IsSet( PossibleOptions.OptionOne ) ) {
    //optionOne is one of those set
}

Saya menemukan ini lebih mudah untuk dibaca daripada kebanyakan cara memeriksa bendera yang disertakan.


30
2017-08-12 18:40



@Nidonocu

Untuk menambahkan bendera lain ke kumpulan nilai yang ada, gunakan operator ATAU penugasan.

Mode = Mode.Read;
//Add Mode.Write
Mode |= Mode.Write;
Assert.True(((Mode & Mode.Write) == Mode.Write)
  && ((Mode & Mode.Read) == Mode.Read)));

19
2017-08-12 15:37



Dalam ekstensi untuk jawaban yang diterima, di C # 7 bendera enum dapat ditulis menggunakan literal biner:

[Flags]
public enum MyColors
{
    None   = 0b0000,
    Yellow = 0b0001,
    Green  = 0b0010,
    Red    = 0b0100,
    Blue   = 0b1000
}

Saya pikir representasi ini menjelaskan bagaimana bendera bekerja di bawah selimut.


16
2017-07-20 11:09



Menambahkan Mode.Write:

Mode = Mode | Mode.Write;

15
2017-08-12 05:59



Ada sesuatu yang terlalu berbelit-belit kepada saya tentang if ((x & y) == y)... membangun, terutama jika x DAN y keduanya merupakan kumpulan bendera gabungan dan Anda hanya ingin tahu jika ada apa saja tumpang tindih.

Dalam hal ini, semua yang benar-benar perlu Anda ketahui adalah jika ada nilai bukan nol [1] setelah Anda menggigitnya.

[1] Lihat komentar Jaime. Jika kami benar-benar asli bitmaskingkami akan   hanya perlu memeriksa apakah hasilnya positif. Tapi sejak itu enums   bisa negatif, bahkan, anehnya, ketika dikombinasikan dengan itu [Flags]   atribut,   itu defensif untuk kode != 0 daripada > 0.

Membangun dari pengaturan @ andnil ...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BitFlagPlay
{
    class Program
    {
        [Flags]
        public enum MyColor
        {
            Yellow = 0x01,
            Green = 0x02,
            Red = 0x04,
            Blue = 0x08
        }

        static void Main(string[] args)
        {
            var myColor = MyColor.Yellow | MyColor.Blue;
            var acceptableColors = MyColor.Yellow | MyColor.Red;

            Console.WriteLine((myColor & MyColor.Blue) != 0);     // True
            Console.WriteLine((myColor & MyColor.Red) != 0);      // False                
            Console.WriteLine((myColor & acceptableColors) != 0); // True
            // ... though only Yellow is shared.

            Console.WriteLine((myColor & MyColor.Green) != 0);    // Wait a minute... ;^D

            Console.Read();
        }
    }
}

13
2018-05-01 23:57