C # [Flags] Enum Attribute anlamı ne?


1445

Zaman zaman aşağıdaki gibi bir numaralandırma görüyorum:

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

[Flags]Özelliğin tam olarak ne yaptığını anlamıyorum .

Herkes iyi bir açıklama ya da örnek gönderebilir?


Kabul edilen cevaba ek olarak, VB.NET'in aslında [Bayraklar] gerektirdiğini de belirtmek gerekir - en azından .NET adamlarına göre: social.msdn.microsoft.com/forums/en-US/csharplanguage/thread/…
Rushyo

8
Not, bugünlerde VB'de gerekli değildir. Davranışı C # olarak kaydet - sadece ToString () çıktısını değiştirir. Ayrıca, Enum'un İÇİNDE mantıksal VEYA yapabilirsiniz. Çok havalı. Kedi = 1, Köpek = 2, CatAndDog = Kedi || Köpek.
Chalky

14
@Chalky CatAndDog = Cat | Dog(Koşullu yerine mantıksal VEYA) demek istediniz ?
DdW

5
@DdW, kısmen doğru: | kullanılmalıdır, ancak | ikili VEYA olarak adlandırılır. II mantıksal
OR'dir

Yanıtlar:


2152

[Flags]Numaralandırılabilir yerine tek bir değerden, olası değerler koleksiyonunu temsil her özellik kullanılmalıdır. Bu tür koleksiyonlar genellikle bitsel operatörlerle kullanılır, örneğin:

var allowedColors = MyColor.Red | MyColor.Green | MyColor.Blue;

[Flags]Özniteliğin bunu tek başına etkinleştirmediğini unutmayın - tek yaptığı .ToString()yöntemle güzel bir temsile izin vermektir :

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"

Ayrıca, enum değerlerinin otomatik olarak ikisinin gücünü [Flags] yapmadığını da belirtmek önemlidir . Sayısal değerleri atlarsanız, enum bitsel işlemlerde beklendiği gibi çalışmaz, çünkü varsayılan olarak değerler 0 ve artışla başlar.

Yanlış beyan:

[Flags]
public enum MyColors
{
    Yellow,  // 0
    Green,   // 1
    Red,     // 2
    Blue     // 3
}

Bu şekilde bildirilen değerler Sarı = 0, Yeşil = 1, Kırmızı = 2, Mavi = 3 olacaktır. Bu, bayrak olarak işe yaramaz hale gelir.

İşte doğru bir beyan örneği:

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

Mülkünüzdeki farklı değerleri almak için bunu yapabilirsiniz:

if (myProperties.AllowedColors.HasFlag(MyColor.Yellow))
{
    // Yellow is allowed...
}

veya .NET 4'ten önce:

if((myProperties.AllowedColors & MyColor.Yellow) == MyColor.Yellow)
{
    // Yellow is allowed...
}

if((myProperties.AllowedColors & MyColor.Green) == MyColor.Green)
{
    // Green is allowed...
}    

Kapakların altında

Bu işe yarıyor çünkü numaralandırmanızda iki güç kullandınız. Kapakların altında, numaralandırma değerleriniz ikili ve sıfırlarda şöyle görünür:

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

Benzer şekilde, ikili bitwise OR operatörünü kullanarak AllowColors mülkünüzü Kırmızı, Yeşil ve Mavi olarak ayarladıktan sonra |, AllowColors şöyle görünür:

myProperties.AllowedColors: 00001110

Bu nedenle, değeri aldığınızda, gerçekte bitsel ve performans &değerleri üzerinde çalışıyorsunuz:

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

Yok = 0 değeri

0Numaralandırmanızda kullanımla ilgili olarak, MSDN'den alıntı yaparak:

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

Değeri sıfır olan bayrak numaralandırma sabiti adı olarak Yok'u kullanın. Sonuç her zaman sıfır olduğu için bir bayrağı test etmek için Yok numaralandırılmış sabiti bit yönünde VE işleminde kullanamazsınız. Bununla birlikte, sayısal değerdeki herhangi bir bitin ayarlanıp ayarlanmadığını belirlemek için, sayısal değer ile Hiçbiri numaralandırılmamış sabiti arasında bitsel olarak değil mantıksal bir karşılaştırma yapabilirsiniz.

Daha bayraklar özelliği hakkında bilgi ve onun kullanımını bulabilirsiniz msdn ve msdn de tasarlama bayrakları


156
Bayrakların kendisi hiçbir şey yapmaz. Ayrıca, C # Bayrak başına tek başına gerektirmez. Ama ToStringenum uygulanması bu yüzden Flags kullanır ve Enum.IsDefined, Enum.Parsevb MyColor.Yellow sonucu en Bayraklar ve göz çıkarmak için deneyin | MyColor.Red; onsuz "5", Bayraklar ile "Sarı, Kırmızı" olsun. Çerçevenin diğer bazı bölümlerinde de [Bayraklar] kullanılır (ör. XML Serileştirme).
Ruben

7
// Sarı ayarlandı ..., biraz yanıltıcı buluyorum. Hiçbir şey ayarlanmadı, bu Sarı'nın İzin Verilen Renklerinizin bir üyesi olduğu anlamına gelir, belki de daha iyi olur / Sarıya izin verilir?
Scott Weaver

48
A = 1 << 0, B = 1 << 1, C = 1 << 2 şeklindeki sabitleri kullanmayı tercih ederim ... Okumak, anlamak, görsel olarak kontrol etmek ve değiştirmek çok daha kolay.
Nick Westgate

2
@borrrden, cehennem evet! Bunu buldum: msdn.microsoft.com/en-us/library/system.enum.aspx - "Açıklamalar" bölümüne bakın: "Enum, .NET Framework'teki tüm numaralandırmalar için temel sınıftır." ve "Sayım, Enum'dan açıkça miras almaz; kalıtım ilişkisi derleyici tarafından dolaylı olarak ele alınır." Yani, yazarken: public enum bla bla bla - bu bir değer türüdür. Ancak, HasFlag yöntemi ona bir sınıf (referans türü :) olan System.Enum örneğini vermenizi istiyor
Aleksei Chepovoi

2
Bir bayrağı numaralandırma dışında bırakmak istiyorsanız, C # 'da ^ olan xor kullanın. Eğer varsa myProperties.AllowedColors = MyColor.Red | MyColor.Green | MyColor.Blue;. Yapabilirsiniz:myProperties.AllowedColors = myProperties.AllowedColors ^ MyColor.Blue //myProperties.AllowedColors == MyColor.Red | Mycolor.Green
Josh Noe

779

Bunu da yapabilirsiniz

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

Bit kaydırmayı 4,8,16,32 yazmaktan daha kolay buluyorum. Kodunuz üzerinde hiçbir etkisi yoktur, çünkü hepsi derleme zamanında yapılır


18
Demek istediğim, kaynak kodunda tam int olmasını tercih ediyorum. Eğer veritabanında bir numaralandırma depolayan bir değeri depolar ve bir kayıt 131,072 olan MyEnum adlı bir sütun varsa, ben hesap değeri 1 ile enum karşılık geldiğini anlamak gerekir. << 17. Kaynakta yazılmış değeri sadece 131.072 görmenin aksine.
JeremyWeir

6
@jwg Kabul ediyorum, bunun çalışma zamanı performansı hakkında aşırı endişelenmek aptalca, ama aynı şey, bunun enum'u kullandığınız herhangi bir yere bitshifts eklemeyeceğini bilmek güzel olduğunu düşünüyorum. Performansla ilgili herhangi bir şeyden ziyade 'bu temiz' bir şeyden daha fazlası
Orion Edwards

18
@JeremyWeir - Bir bayrak numaralandırma değerinde birkaç bit ayarlanacaktır. Veri analizi yönteminiz yanlış olan şeydir. Tam sayı değerinizi ikili olarak temsil etmek için bir yordam çalıştırın. 131,072 [D] = 0000 0000 0000 0001 0000 0000 0000 0000 0000 [B] [32] .. 17. bit ayarlandığında, 1 << 17 atanan bir enum değeri kolayca belirlenebilir. 0110 0011 0011 0101 0101 0011 0101 1011 [b] [32] .. 1 << 31, 1 << 30, 1 << 26, 1 << 25 .. vb. Atanan numaralandırma değerleri, hiç bir hesap yardımı .. ki ben ikili temsilcisi almadan hiç belirlemek mümkün olacağını şüpheliyim.
Brett Caswell

13
Ayrıca, doğrudan sayılardan ziyade "önceki" numaralandırma değerlerinden, yani Third = Second << 1yerine Third = 1 << 2- biraz daha
kaydırma yapabileceğinizi belirtmek

26
Bu gibi ama altta yatan enum türü üst sınırlarına aldıktan sonra, derleyici gibi biraz kaymalarına karşı uyarmak değil 1 << 31 == -2147483648, 1 << 32 == 1, 1 << 33 == 2, vb. Aksine, ThirtySecond = 2147483648int tipi bir enum için derlerseniz , derleyici bir hata atar.
aaaantoine

115

Cevapları birleştiren https://stackoverflow.com/a/8462/1037948 (deklarasyon bit kayması yoluyla) ve https://stackoverflow.com/a/9117/1037948 (bildiriminde kombinasyonları kullanarak) Bunun yerine bit kaydırma önceki değerler can rakamları kullanmaktan daha iyidir. Mutlaka tavsiye etmek değil, sadece işaret edebilirsiniz.

Ziyade:

[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,
}

Beyan edebilirsin

[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,
}

LinqPad ile Onaylama:

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

Sonuçlar:

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

23
Kombinasyonlar iyi bir öneri, ama zincirleme bit-shift iki = bir << 1, üç = bir << 1, vb gibi kopyala-yapıştır hataları eğilimli olacağını düşünüyorum ... form 1 << n daha güvenlidir ve amaç daha açıktır.
Rupert Rawnsley

2
Cevabımı alıntılamak için @RupertRawnsley:> Her zaman tavsiye etmiyorum, ama sadece işaret edebilirsiniz
drzaus

49

Beyan ve olası kullanımı gösteren bir örnek için lütfen aşağıdakilere bakın:

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");
            }
        }
    }
}

Bu örnek, [Bayraklar] dışında kalsanız bile çalışır. Öğrenmeye çalıştığım [Bayraklar] kısmı.
gbarry

39

Kabul edilen cevaba ek olarak, C # 7'de numaralandırma bayrakları ikili değişmez değerler kullanılarak yazılabilir:

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

Bence bu temsil bayrakların örtü altında nasıl işlediğini açıkça ortaya koyuyor .


2
Ve C # 7.2'de önde gelen ayırıcı ile daha da net ! 0b_0100
Vincenzo Petronio

37

Ben son zamanlarda sorulan benzer bir şey hakkında.

Bayraklar kullanıyorsanız, içerilen bayrakları kontrol etmeyi kolaylaştırmak için numaralandırmalara bir uzatma yöntemi ekleyebilirsiniz (ayrıntılar için gönderiye bakın)

Bu şunları yapmanızı sağlar:

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

Sonra şunları yapabilirsiniz:

PossibleOptions opt = PossibleOptions.OptionOneTwoAndThree 

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

Bunu okumayı, dahil olan bayrakları kontrol etmenin en yollarından daha kolay buluyorum.


IsSet benimsediğim bir uzantı yöntemi mi?
Robert MacLean

Evet - detaylar için link verdiğim diğer soruyu okuyun: stackoverflow.com/questions/7244
Keith

71
.NET 4 HasFlag, numaralandırmalara bir yöntem ekler , böylece opt.HasFlag( PossibleOptions.OptionOne )kendi uzantılarınızı yazmak zorunda kalmadan yapabilirsiniz
Orion Edwards

1
Not HasFlagolduğunu çok daha yavaş bitsel işlemleri yapmaktan daha.
Wai Ha Lee

1
@WaiHaLee İfade kullanılarak hızlı bir sürüme derlenen CodeJam.EnumHelper.IsFlagSet uzantı yöntemini kullanabilirsiniz: github.com/rsdn/CodeJam/wiki/M_CodeJam_EnumHelper_IsFlagSet__1
NN_

22

@Nidonocu

Mevcut bir değer kümesine başka bir bayrak eklemek için VEYA atama işlecini kullanın.

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

18

Bayraklarla çalışırken genellikle ek Yok ve Tüm öğeleri bildiririm. Bunlar, tüm bayrakların ayarlanıp ayarlanmadığını veya herhangi bir bayrak ayarlanmadığını kontrol etmeye yardımcı olur.

[Flags] 
enum SuitsFlags { 

    None =     0,

    Spades =   1 << 0, 
    Clubs =    1 << 1, 
    Diamonds = 1 << 2, 
    Hearts =   1 << 3,

    All =      ~(~0 << 4)

}

Kullanımı:

Spades | Clubs | Diamonds | Hearts == All  // true
Spades & Clubs == None  // true

 
2019-10 Güncellemesi:

C # 7.0'dan beri, muhtemelen daha sezgisel olan ikili değişmezleri kullanabilirsiniz:

[Flags] 
enum SuitsFlags { 

    None =     0b0000,

    Spades =   0b0001, 
    Clubs =    0b0010, 
    Diamonds = 0b0100, 
    Hearts =   0b1000,

    All =      0b1111

}


14

if ((x & y) == y)...Yapı hakkında bana aşırı ayrıntılı bir şey var , özellikle xVE yher ikisi de bileşik bayrak kümeleriyse ve sadece herhangi bir çakışma olup olmadığını bilmek istiyorsanız .

Bu durumda, bilmeniz gereken tek şey, bitmaskledikten sonra sıfır olmayan bir değer [1] olup olmadığıdır .

[1] Bkz. Jaime'nin yorumu. Eğer otantik bir şekilde bitmasaydık , sadece sonucun pozitif olup olmadığını kontrol etmemiz gerekirdi . Fakat enumler negatif olabilir birleştirildiğinde bile, garip, öznitelik , onun için koda savunma var ziyade .[Flags]!= 0> 0

@ Andnil kurulumundan çıkılıyor ...

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();
        }
    }
}

Enum imzalı bir türe bağlı olabilir, bu nedenle "> 0" yerine "! = 0" kullanmalısınız.
kuzgun

@JaimePardos - Onları dürüst bayt tuttuğumuz sürece, bu örnekte yaptığım gibi, olumsuz bir kavram yoktur. Yalnızca 0 - 255 arasında. MSDN'in uyardığı gibi , "Negatif bir sayıyı sabit olarak numaralandırılmış bir bayrak olarak tanımlarsanız dikkatli olun çünkü ... [bu] kodunuzu kafa karıştırıcı hale getirebilir ve kodlama hatalarını teşvik edebilir." "Olumsuz bitflags" açısından düşünmek garip! ; ^) Biraz daha düzenleyeceğim. Ama haklısın, eğer negatif değerler kullanırsak enum, kontrol etmeliyiz != 0.
Ağustos'ta

10

Bayraklar, numaralandırmanızın içinde bit maskelemeyi kullanmanızı sağlar. Bu, hangilerinin belirtildiğini korurken numaralandırma değerlerini birleştirmenizi sağlar.

[Flags]
public enum DashboardItemPresentationProperties : long
{
    None = 0,
    HideCollapse = 1,
    HideDelete = 2,
    HideEdit = 4,
    HideOpenInNewWindow = 8,
    HideResetSource = 16,
    HideMenu = 32
}

0

Birisi bu senaryoyu fark etmiş olsa bile özür dileriz. Yansımada görebildiğimiz bayraklara mükemmel bir örnek. Evet Bağlayıcı Bayraklar ENUM. https://docs.microsoft.com/en-us/dotnet/api/system.reflection.bindingflags?view=netframework-4.8

[System.Flags]
[System.Runtime.InteropServices.ComVisible(true)]
[System.Serializable]
public enum BindingFlags

kullanım

             // BindingFlags.InvokeMethod
            // Call a static method.
            Type t = typeof (TestClass);

            Console.WriteLine();
            Console.WriteLine("Invoking a static method.");
            Console.WriteLine("-------------------------");
            t.InvokeMember ("SayHello", BindingFlags.InvokeMethod | BindingFlags.Public | 
                BindingFlags.Static, null, null, new object [] {});
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.