Numaralandırma izinleri neden genellikle 0, 1, 2, 4 değerlerine sahiptir?


159

İnsanlar neden daima enum değerlerini kullanıyor 0, 1, 2, 4, 8ve kullanmıyor 0, 1, 2, 3, 4?

Bunun bit işlemleri vb. İle bir ilgisi var mı?

Bunun nasıl doğru kullanıldığı konusunda küçük bir örnek snippet'i gerçekten takdir ediyorum :)

[Flags]
public enum Permissions
{
    None   = 0,
    Read   = 1,
    Write  = 2,
    Delete = 4
}


25
Dupe oyuna katılmıyorum.
zzzzBov

İzin ayarlamanın UNIX yolu da aynı mantığa dayanır.
Rudy

3
@Pascal: Bunu yararlı okumayı bulabilirsiniz bit OR (ve ikilik VE budur), |(ve &) temsil eder. Çeşitli cevaplar bunu bildiğinizi varsayar.
Brian

2
@IAdapter Her ikisinin cevabı aynı olduğundan neden düşündüğünüzü anlayabiliyorum, ancak soruların farklı olduğunu düşünüyorum. Diğer soru sadece C # 'daki Flags özniteliğinin bir örneğini veya açıklamasını ister. Bu soru bit bayrakları kavramı ve bunların arkasındaki temeller ile ilgili gibi görünüyor.
Jeremy S

Yanıtlar:


268

Çünkü onlar ikisinin gücüdür ve bunu yapabilirim:

var permissions = Permissions.Read | Permissions.Write;

Ve belki sonra ...

if( (permissions & Permissions.Write) == Permissions.Write )
{
    // we have write access
}

Her bitin bazı izinlere (veya numaralandırılmış değerin mantıksal olarak karşılık geldiği her neyse) karşılık geldiği bir bit alanıdır. Bunlar olarak tanımlanmış 1, 2, 3, ...olsaydınız, bu şekilde bitsel operatörleri kullanamaz ve anlamlı sonuçlar elde edemezdiniz. Daha derine inmek için ...

Permissions.Read   == 1 == 00000001
Permissions.Write  == 2 == 00000010
Permissions.Delete == 4 == 00000100

Burada bir model fark ettiniz mi? Şimdi orijinal örneğimi alırsak, yani,

var permissions = Permissions.Read | Permissions.Write;

Sonra...

permissions == 00000011

Görmek? Hem Readve Writebit'lerinin ve bunu bağımsız kontrol edebilirsiniz (yani Ayrıca haber Deletebit edilir değil ayarlamak ve dolayısıyla bu değer silmek için izniniz iletmek değildir).

Tek bir bit alanında birden çok bayrak saklanmasına izin verir.


2
@ Malcolm: Öyle; myEnum.IsSet. Bunun tamamen işe yaramaz bir soyutlama olduğunu ve sadece yazmayı azaltmaya hizmet ettiğini düşünüyorum, ancak meh
Ed S.

1
İyi yanıt, ancak Flags özniteliğinin neden uygulandığını ve bazı numaralandırmalara da Bayrak uygulamak istemediğinizi belirtmelisiniz.
Andy

3
@Andy: Aslında, Flagsözellik size 'güzel baskı' iirc vermekten biraz daha fazlasını yapar. Numaralandırılmış bir değeri, özelliğin varlığına bakılmaksızın bayrak olarak kullanabilirsiniz.
Ed

3
@detly: Çünkü C # ifadeleri bir boole ifadesi gerektiriyorsa. 0değil false; falseolduğunu false. Ancak yazabilirsiniz if((permissions & Permissions.Write) > 0).
Ed

2
'Zor' yerine, (permissions & Permissions.Write) == Permissions.Writeartık kullanabilirsinizenum.HasFlag()
Louis Kottmann

147

Diğer cevaplardan hala net değilse, şöyle düşünün:

[Flags] 
public enum Permissions 
{   
   None = 0,   
   Read = 1,     
   Write = 2,   
   Delete = 4 
} 

yazmanın sadece daha kısa bir yoludur:

public enum Permissions 
{   
    DeleteNoWriteNoReadNo = 0,   // None
    DeleteNoWriteNoReadYes = 1,  // Read
    DeleteNoWriteYesReadNo = 2,  // Write
    DeleteNoWriteYesReadYes = 3, // Read + Write
    DeleteYesWriteNoReadNo = 4,   // Delete
    DeleteYesWriteNoReadYes = 5,  // Read + Delete
    DeleteYesWriteYesReadNo = 6,  // Write + Delete
    DeleteYesWriteYesReadYes = 7, // Read + Write + Delete
} 

Sekiz olasılık var, ancak bunları sadece dört üyenin birleşimi olarak temsil edebilirsiniz. On altı olasılık olsaydı, onları sadece beş üyenin birleşimi olarak temsil edebilirdin. Dört milyar olasılık olsaydı, onları sadece 33 üyeden oluşan kombinasyonlar olarak temsil edebilirsin! Her biri (sıfır hariç) sadece 33 üyeye sahip olmak, bir numarada dört milyar maddeyi adlandırmaya çalışmaktan çok daha iyidir.


32
enumDört milyar üyeli zihinsel görüntü için +1 . Ve üzücü kısmı, muhtemelen orada biri denedi.
Daniel Pryden

23
@DanielPryden Daily WTF'in günlük okuyucusu olarak buna inanırdım.
kabarık

1
2 ^ 33 = ~ 8.6 milyar. 4 milyar farklı değer için sadece 32 bite ihtiyacınız vardır.
CVn

5
@ MichaelKjörling 33'ten biri 0 varsayılanı için
cırcır ucube

@ MichaelKjörling: Adil olmak gerekirse, 2'nin gücü olan sadece 32 üye var, çünkü 0 ikinin gücü değil. Yani "33 üye, her birinin ikisinin gücü" tam olarak doğru değildir 2 ** -infinity(ikisinin gücü olarak saymadıkça).
Brian

36

Bu değerler ikili dosyadaki benzersiz bit konumlarını temsil ettiğinden:

1 == binary 00000001
2 == binary 00000010
4 == binary 00000100

vb.

1 | 2 == binary 00000011

DÜZENLE:

3 == binary 00000011

İkilikteki 3 değeri, hem yer hem de ikişer yerde 1 değeriyle temsil edilir. Aslında değerle aynı 1 | 2. İkili yerleri bazı devletleri temsil etmek için bayrak olarak kullanmaya çalıştığınızda, 3 genellikle anlamlı değildir (aslında ikisinin birleşimi olan mantıksal bir değer yoksa)

Daha fazla açıklama için örnek numaralandırmanızı aşağıdaki gibi genişletmek isteyebilirsiniz:

[Flags]
public Enum Permissions
{
  None = 0,   // Binary 0000000
  Read = 1,   // Binary 0000001
  Write = 2,  // Binary 0000010
  Delete = 4, // Binary 0000100
  All = 7,    // Binary 0000111
}

Bu nedenle ben de Permissions.All, ben de örtülü olarak var Permissions.Read, Permissions.WritevePermissions.Delete


ve 2 | 3 ile ilgili sorun nedir?
Pascal

1
@Pascal: Çünkü 3olduğunu 11, yani ikili bir anlamlı değere keyfi pozisyonda 1 bit haritasına yeteneğini kaybedebilir yüzden, o tek dizi bit eşleşmiyor.
Ed S.

8
@Pascal başka bir yol koydu 2|3 == 1|3 == 1|2 == 3. Eğer ikili birlikte bir değer var ise 00000011, ve bayrakları değerleri dahil 1, 2ve 3, o zaman bu değeri temsil ediyorsa bilemeyiz 1 and 3, 2 and 3, 1 and 2veya only 3. Bu onu çok daha az kullanışlı hale getirir.
yshavit

10
[Flags]
public Enum Permissions
{
    None   =    0; //0000000
    Read   =    1; //0000001
    Write  = 1<<1; //0000010
    Delete = 1<<2; //0000100
    Blah1  = 1<<3; //0001000
    Blah2  = 1<<4; //0010000
}

Bunun gibi yazmanın anlaşılması ve okunması daha kolay olduğunu ve bunu hesaplamanıza gerek olmadığını düşünüyorum.


5

Bunlar, enum değerlerinin kombinasyonlarına izin veren bit bayraklarını temsil etmek için kullanılır. Değerleri onaltılı gösterimde yazarsanız daha net olduğunu düşünüyorum

[Flags]
public Enum Permissions
{
  None =  0x00,
  Read =  0x01,
  Write = 0x02,
  Delete= 0x04,
  Blah1 = 0x08,
  Blah2 = 0x10
}

4
@Pascal: Belki de bu noktada sizin için daha okunabilir, ancak onaltılık görüntüleme baytları deneyim kazandıkça ikinci doğa olur. Onaltılık haritadaki iki hane bir baytla 8 bit eşleştirir (iyi ... bir bayt genellikle 8 bittir ... her zaman doğru değildir, ancak bu örnek için genelleme tamamdır).
Ed S.

5
@Pascal hızlı, 2 ile çarptığınızda ne elde edersiniz 4194304? Nasıl 0x400000? 0x800000Doğru cevap olarak tanımlamak çok daha kolaydır 8388608ve onaltılık değeri yazmak daha az hataya açıktır.
phoog

6
Bir bakışta, bayraklarınızın düzgün ayarlandığını (yani, 2'nin gücü olup olmadığını), onaltılık kullanırsanız, anlatmak çok daha kolaydır. 0x10000İki güç var mı ? Evet, 1, 2, 4 veya 8 ile başlar ve daha sonra tüm 0'lara sahiptir. Zihinsel olarak 0x10'u 16'ya çevirmenize gerek yoktur (bunu yapmak muhtemelen sonunda ikinci doğa olacaktır), sadece "2'nin bir gücü" olarak düşünün.
Brian

1
Ben kesinlikle Jared onaltılık not çok daha kolay olması ile birlikte. sadece 1 2 4 8 kullanın ve vardiya kullanın
bevacqua

1
Şahsen ben sadece örneğin P_READ = 1 << 0, P_WRITE = 1 <, 1, P_RW = P_READ | P_WRITE kullanmayı tercih ederim. Sabit katlama bu tür C # çalışır emin değilim ama C / C ++ (yanı sıra Java, bence) gayet iyi çalışıyor.
kabarık

1

Bu daha çok bir yorum, ancak bu biçimlendirmeyi desteklemeyeceği için, bayrak numaralandırmalarını ayarlamak için kullandığım bir yöntemi dahil etmek istedim:

[Flags]
public enum FlagTest
{
    None = 0,
    Read = 1,
    Write = Read * 2,
    Delete = Write * 2,
    ReadWrite = Read|Write
}

Bayraklarınızı alfabetik sırayla korumak istediğinizde bu yaklaşımı özellikle geliştirme sırasında faydalı buluyorum. Yeni bir bayrak değeri eklemeniz gerektiğini belirlerseniz, bunu sadece alfabetik olarak ekleyebilirsiniz ve değiştirmek zorunda olduğunuz tek değer, bundan önce olduğu değerdir.

Bununla birlikte, herhangi bir üretim sistemine bir çözüm yayınlandıktan sonra (özellikle numaralandırma bir web servisi gibi sıkı bir bağlantı olmadan açığa çıkarsa), enum içindeki mevcut herhangi bir değerin değiştirilmesine karşı kesinlikle tavsiye edilir.


1

Bu konuda çok iyi cevaplar… Sadece söyleyeceğim .. Eğer sözdiziminin neyi ifade etmeye çalıştığını sevmezseniz veya kolayca kavrayamazsanız<< .. Şahsen bir alternatifi tercih ederim (ve diyorum ki, basit numaralandırma beyanı stili) ...

typedef NS_OPTIONS(NSUInteger, Align) {
    AlignLeft         = 00000001,
    AlignRight        = 00000010,
    AlignTop          = 00000100,
    AlignBottom       = 00001000,
    AlignTopLeft      = 00000101,
    AlignTopRight     = 00000110,
    AlignBottomLeft   = 00001001,
    AlignBottomRight  = 00001010
};

NSLog(@"%ld == %ld", AlignLeft | AlignBottom, AlignBottomLeft);

LOG 513 == 513

Anlamak çok daha kolay (en azından kendim için). Bunları sıralayın… istediğiniz sonucu tanımlayın, istediğiniz sonucu alın .. Gerekli "hesaplamalar" yok.

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.