열거 형 권한에 종종 0, 1, 2, 4 값이있는 이유는 무엇입니까?


159

사람들은 왜 항상 열거 값이 좋아 사용 0, 1, 2, 4, 8하지 0, 1, 2, 3, 4?

비트 연산 등과 관련이 있습니까?

이것이 올바르게 사용되는 방법에 대한 작은 샘플 스 니펫에 감사드립니다. :)

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


25
나는 속임수 투표에 동의하지 않습니다.
zzzzBov

권한을 설정하는 UNIX 방법도 동일한 논리를 기반으로합니다.
Rudy

3
@Pascal : 당신은 도움에 대해 읽고 찾을 수 있습니다 비트 단위 OR (와 비트 단위 AND 무엇을), |(그리고 &)를 나타냅니다. 다양한 답변은 당신이 그것에 익숙하다고 가정합니다.
Brian

2
@IAdapter 나는 둘 다에 대한 대답이 동일하기 때문에 왜 그렇게 생각하는지 알 수 있지만 질문이 다르다고 생각합니다. 다른 질문은 C #의 Flags 속성에 대한 예제 또는 설명을 요구합니다. 이 질문은 비트 플래그의 개념과 그 뒤에있는 기본 사항에 관한 것 같습니다.
Jeremy S

답변:


268

그들은 2의 거듭 제곱이고 나는 이것을 할 수 있기 때문에 :

var permissions = Permissions.Read | Permissions.Write;

아마 나중에 ...

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

비트 필드이며, 각 세트 비트는 일부 권한 (또는 열거 된 값이 논리적으로 일치하는 것)에 해당합니다. 이것이 정의 된 1, 2, 3, ...경우이 방식으로 비트 연산자를 사용할 수없고 의미있는 결과를 얻을 수 없습니다. 더 깊이 탐구하려면 ...

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

여기에 패턴이 있습니까? 이제 원래 예를 들어 보면

var permissions = Permissions.Read | Permissions.Write;

그때...

permissions == 00000011

보다? 모두 ReadWrite비트가 설정되고, 그 독립적으로 확인할 수 있습니다합니다 (있음 또한 통지 Delete비트가되어 있지 설정을하기 때문에이 값은 삭제할 수있는 권한을 전달하지 않습니다).

하나의 비트 필드에 여러 플래그를 저장할 수 있습니다.


2
@ Malcolm : 그렇습니다. myEnum.IsSet. 나는 이것이 완전히 쓸모없는 추상화이며 타이핑을 줄이는 데만 도움이된다고 생각하지만 meh
Ed S.

1
좋은 대답이지만 Flags 속성이 적용되는 이유와 플래그를 일부 열거 형에 적용하고 싶지 않은 경우를 언급해야합니다.
Andy

3
@Andy : 사실,이 Flags속성은 당신에게 '예쁜 인쇄'iirc를주는 것 이상을 수행하지 않습니다. 속성의 존재 여부에 관계없이 열거 된 값을 플래그로 사용할 수 있습니다.
Ed S.

3
@detly : C #의 if 문에 부울식이 필요하기 때문입니다. 0아니다 false; false입니다 false. 그러나 당신은 쓸 수 if((permissions & Permissions.Write) > 0)있습니다.
Ed S.

2
'tricky'대신 (permissions & Permissions.Write) == Permissions.Write이제 다음을 사용할 수 있습니다.enum.HasFlag()
Louis Kottmann

147

다른 답변에서 여전히 명확하지 않은 경우 다음과 같이 생각하십시오.

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

작성하는 더 짧은 방법입니다.

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
} 

여덟 가지 가능성이 있지만 네 멤버 만 조합하여 나타낼 수 있습니다. 16 개의 가능성이 있다면 5 명의 조합으로 나타낼 수 있습니다. 40 억 개의 가능성이 있다면 33 명의 조합으로 나타낼 수 있습니다! 열거 형에 40 억 개의 항목을 명명하려고 시도하는 것보다 각각 33의 구성원 (각 0을 제외하고)의 2 제곱을 갖는 것이 훨씬 낫습니다.


32
enum40 억 명의 회원 이있는 정신적 이미지 +1 그리고 슬픈 부분은 아마도 누군가가 그것을 시도했을 것입니다.
Daniel Pryden

23
@DanielPryden Daily WTF의 매일 독자로서, 나는 그것을 믿을 것입니다.
솜털 같은

1
2 ^ 33 = ~ 88 억. 40 억 개의 다른 값에는 32 비트 만 필요합니다.
CVn

5
@ MichaelKjörling 33 중 하나는 0 기본입니다
래칫 괴물

@ MichaelKjörling : 공정하게 말하면 0은 2의 거듭 제곱이 아니기 때문에 2의 거듭 제곱 인 멤버는 32 명뿐입니다. 따라서 "33 인, 각 1의 거듭 제곱"은 정확하지 않습니다 ( 2 ** -infinity2의 거듭 제곱으로 계산하지 않는 한 ).
Brian

36

이 값들은 이진수로 고유 한 비트 위치를 나타 내기 때문에 :

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

등등

1 | 2 == binary 00000011

편집하다:

3 == binary 00000011

이진수로 3은 1과 2의 위치에서 1의 값으로 표시됩니다. 실제로 값과 같습니다 1 | 2. 따라서 이진 자리를 플래그로 사용하여 일부 상태를 나타내는 경우 3은 의미가 없습니다 (실제로 두 값의 조합 인 논리적 값이없는 한)

더 명확히하기 위해 다음과 같이 예제 열거 형을 확장 할 수 있습니다.

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

따라서,에는 Permissions.All또한 암시 적 Permissions.Read으로 Permissions.Write, 및Permissions.Delete


그리고 2 | 3의 문제점은 무엇입니까?
Pascal

1
@Pascal : 때문에 3이다 11, 즉 바이너리는 의미있는 값으로 임의의 위치에 1 개 비트를지도 할 수있는 능력을 상실하므로,이, 한 세트의 비트에 매핑되지 않습니다.
Ed S.

8
@Pascal은 다른 방법으로 설명 2|3 == 1|3 == 1|2 == 3합니다. 당신이 바이너리 값을 가질 경우에 따라서 00000011, 당신의 플래그 값을 포함 1, 2그리고 3, 당신은 그 값을 나타내는 경우 모르겠다 1 and 3, 2 and 3, 1 and 2또는 only 3. 그것은 훨씬 덜 유용합니다.
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
}

나는 이런 식으로 글을 이해하고 읽기가 더 쉽다고 생각하며 계산할 필요가 없습니다.


5

이들은 열거 형 값의 조합을 허용하는 비트 플래그를 나타내는 데 사용됩니다. 16 진수 표기법으로 값을 쓰면 더 명확하다고 생각합니다.

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

4
@Pascal : 아마도이 시점에서 더 읽기 쉬울 것입니다. 그러나 16 진수로 바이트를 보는 경험이 두 번째 특성이됩니다. 16 진수의 두 자리 숫자는 1 바이트에 매핑됩니다 (음 ... 바이트는 어쨌든 8 비트입니다 ... 항상 사실은 아니지만이 예제에서는 일반화해도 좋습니다).
Ed S.

5
@Pascal 빨리, 41943042 를 곱하면 무엇을 얻 습니까? 어때요 0x400000? 0x800000보다 정답 으로 인식 하는 것이 훨씬 쉽고 838860816 진수 값을 입력하는 것이 오류가 적습니다.
phoog

6
16 진수를 사용하는 경우 플래그가 올바르게 설정되었는지 (즉, 2의 거듭 제곱인지) 한눈에 알기가 훨씬 쉽습니다. 가 0x100002의 거듭 제곱은? 예, 1, 2, 4 또는 8로 시작하고 그 후에는 모두 0입니다. 당신은 정신적으로 0x10을 16으로 번역 할 필요가 없습니다. (그렇게하면 결국 제 2의 자연이 될 것입니다.) 단지 "일부 2의 힘"으로 생각하십시오.
Brian

1
나는 16 진수로 훨씬 쉽게 알아볼 수있는 Jared와 절대적으로 관계가 있습니다. 당신은 단지 1 2 4 8을 사용하고 교대
bevacqua

1
개인적으로 P_READ = 1 << 0, P_WRITE = 1 <, 1, P_RW = P_READ | P_WRITE와 같은 것을 선호합니다. 그런 종류의 상수 폴딩이 C #에서 작동하는지 확실하지 않지만 C / C ++ (Java뿐만 아니라)에서도 잘 작동합니다.
솜털 같은

1

이것은 실제로 더 많은 주석이지만 형식을 지원하지 않기 때문에 플래그 열거를 설정하는 데 사용한 방법을 포함하고 싶었습니다.

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

이 접근 방식은 플래그를 알파벳 순서로 유지하려는 경우 개발 중에 특히 유용합니다. 새 플래그 값을 추가해야한다고 결정한 경우 알파벳순으로 삽입 할 수 있으며 변경해야하는 유일한 값은 이전 값입니다.

그러나 솔루션이 프로덕션 시스템에 게시되면 (특히 웹 서비스와 같이 밀접한 결합없이 열거 형이 노출 된 경우) 열거 형 내의 기존 값을 변경하지 않는 것이 좋습니다.


1

이것에 대한 좋은 답변이 많이 있습니다 ... 그냥 말할 것입니다. 구문이 표현하려고하는 것을 좋아하지 않거나 쉽게 파악할 수없는 경우<< 개인적으로 대안을 선호합니다 (그리고 직접 열거 형 선언 스타일을 감히 말합니다 ) …

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

이해하기가 훨씬 쉬워졌습니다. 하나를 정렬하십시오… 원하는 결과를 설명하고, 원하는 결과를 얻으십시오. "계산"이 필요하지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.