C #에서 [Flags] 열거 형 속성의 의미는 무엇입니까?


1445

때때로 나는 다음과 같은 열거 형을 봅니다.

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

[Flags]속성이 정확히 무엇인지 이해 하지 못합니다.

누구나 좋은 설명이나 예를 게시 할 수 있습니까?


VB.NET 에는 실제로 [Flags] 가 필요합니다 . 최소한 .NET 녀석에 따르면 social.msdn.microsoft.com/forums/en-US/csharplanguage/thread/…
Rushyo

8
요즘 VB에는 필요하지 않습니다. 동작을 C #으로 저장-ToString () 출력 만 변경합니다. 열거 형 자체 내에서 논리 OR을 수행 할 수도 있습니다. 매우 시원합니다. 고양이 = 1, 개 = 2, CatAndDog = 고양이 || 개.
Chalky

14
@Chalky 당신은 CatAndDog = Cat | Dog(조건부 대신 논리 OR)을 의미 합니까?
DdW

5
@DdW, 부분적으로 올바른 : | 사용해야하지만 | 이진 OR이라고합니다. II는 논리적 OR (즉, 짧은 단락 - 허용) 적어도 MS에 따른) msdn.microsoft.com/en-us/library/f355wky8.aspx
Pieter21

답변:


2152

[Flags]열거 가능한 오히려 단일 값보다도, 가능한 값들의 집합을 나타낸다마다 속성이 사용되어야한다. 이러한 컬렉션은 종종 비트 연산자와 함께 사용됩니다.

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

[Flags]속성 자체로는이 기능을 사용할 수 없습니다. 이 방법만으로도 .ToString()메소드를 멋지게 표현할 수 있습니다 .

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"

또한 노트에 중요 [Flags] 하지 않습니다 자동으로 두 가지의 열거 값 능력을합니다. 숫자 값을 생략하면 기본적으로 값이 0으로 시작하고 증가하기 때문에 비트 연산에서 예상 할 수있는 것처럼 열거 형이 작동하지 않습니다.

잘못된 선언 :

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

이 방법으로 선언 된 값은 Yellow = 0, Green = 1, Red = 2, Blue = 3입니다. 이렇게하면 값을 플래그로 사용할 수 없게됩니다.

올바른 선언의 예는 다음과 같습니다.

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

속성에서 고유 한 값을 검색하려면 다음을 수행하십시오.

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

또는 .NET 4 : 이전

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

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

덮개 아래

열거 형에 2의 거듭 제곱을 사용했기 때문에 작동합니다. 표지 아래에서 열거 형 값은 이진수 1과 0으로 표시됩니다.

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

마찬가지로 이진 비트 OR 연산자를 사용하여 AllowedColors 속성 을 빨강, 녹색 및 파랑으로 설정하면 AllowedColors 는 다음과 같습니다.|

myProperties.AllowedColors: 00001110

따라서 값을 검색하면 실제로 &값에 대해 비트 단위 AND 를 수행 합니다.

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

None = 0 값

그리고 0MSDN에서 인용하여 열거에서 사용하는 것과 관련하여 :

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

값이 0 인 플래그 열거 상수의 이름으로 None을 사용하십시오. 결과가 항상 0이므로 비트 AND 연산에서 None 열거 상수를 사용하여 플래그를 테스트 할 수 없습니다. 그러나 숫자 값의 비트가 설정되어 있는지 여부를 확인하기 위해 숫자 값과 없음 열거 상수 사이의 비트 단위가 아닌 논리적 비교를 수행 할 수 있습니다.

당신은 더 플래그 특성에 대한 정보와에서의 사용을 찾을 수 MSDNMSDN에서 설계 플래그


156
플래그 자체는 아무것도하지 않습니다. 또한 C #에는 플래그 자체가 필요하지 않습니다. 그러나 ToString열거 형 구현에는 Flags가 사용 Enum.IsDefined되며 Enum.Parse, 등도 사용됩니다. Flags 를 제거하고 MyColor.Yellow | MyColor.Red; 그것 없이는 "5"를 얻습니다. 깃발과 함께 "노란색, 빨간색"을 얻습니다. 프레임 워크의 일부 다른 부분도 [Flags] (예 : XML 직렬화)를 사용합니다.
Ruben

7
// 노란색이 설정되었습니다 ... 약간 오해의 소지가 있습니다. 아무것도 설정되지 않았다는 것은 Yellow가 AllowedColors의 멤버임을 의미합니다. // Yellow가 허용됩니까?
Scott Weaver

48
나는 A = 1 << 0, B = 1 << 1, C = 1 << 2 ... 형식의 상수를 사용하는 것을 선호합니다. 읽고 이해하고 시각적으로 확인하고 변경하는 것이 훨씬 쉽습니다.
Nick Westgate

2
@borrrden, yeah yeahh! 나는 발견이 : msdn.microsoft.com/en-us/library/system.enum.aspx - ". 열거는 .NET Framework의 모든 열거의 기본 클래스입니다" "설명"부분을 참조 "열거 형은 Enum에서 명시 적으로 상속하지 않으며 상속 관계는 컴파일러에 의해 암시 적으로 처리됩니다." 따라서 당신이 쓸 때 : public enum bla bla bla-이것은 가치 유형입니다. 그러나 HasFlag 메소드는 클래스 (참조 유형 :) 인 System.Enum의 인스턴스를 제공하기를 원합니다.
Aleksei Chepovoi

2
열거에서 플래그를 제외하려면 C #에서 ^ 인 xor를 사용하십시오. 당신이 가지고 있다면 myProperties.AllowedColors = MyColor.Red | MyColor.Green | MyColor.Blue;. 당신은 할 수 있습니다 :myProperties.AllowedColors = myProperties.AllowedColors ^ MyColor.Blue //myProperties.AllowedColors == MyColor.Red | Mycolor.Green
조쉬 노

778

당신은 또한 이것을 할 수 있습니다

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

4,8,16,32 등을 입력하는 것보다 비트 시프 팅이 더 쉽다는 것을 알았습니다. 컴파일 타임에 모두 수행되므로 코드에 영향을 미치지 않습니다.


18
그것이 의미하는 바입니다. 소스 코드에서 전체 int를 사용하는 것을 선호합니다. 데이터베이스의 MyEnum이라는 테이블에 열거 형 중 하나의 값을 저장하는 열이 있고 레코드에 131,072가있는 경우 값이 1 인 열거 형에 해당하는 것을 계산하기 위해 계산기를 꺼내야합니다. << 17. 소스에 쓰여진 131,072 값을 보는 것과는 반대로.
JeremyWeir 2016 년

6
@jwg 동의합니다. 런타임 성능에 대해 너무 걱정하는 것은 어리석은 일이지만, 모두 똑같이 열거 형을 사용하는 곳에서 비트 시프트를 삽입하지 않을 것이라는 점을 알고 싶습니다. 보다 오히려 성능에 관련된 어떤 것보다 일이 '깔끔한 그'는의
오리온 에드워즈

18
@JeremyWeir-몇 비트가 플래그 열거 값으로 설정됩니다. 따라서 데이터 분석 방법이 부적절합니다. 정수 값을 이진수로 나타내는 절차를 실행하십시오. 131,072 [D] = 0000 0000 0000 0001 0000 0000 0000 0000 [B] [32]. 17 번째 비트 세트로 1 << 17로 지정된 열거 형 값을 쉽게 결정할 수 있습니다. 0110 0011 0011 0101 0101 0011 0101 1011 [b] [32] .. 열거 형 값 할당 1 << 31, 1 << 30, 1 << 26, 1 << 25 .. 등 등은 계산기의 도움 .. 이진 담당자를 얻지 않고도 전혀 결정할 수 없을 것입니다.
Brett Caswell 5

13
또한 "이전"열거 값이 아니라에서 직접 번호에서 교대 비트, 즉 수 지적 Third = Second << 1하기보다는 Third = 1 << 2참조 - 아래 자세한 설명을
drzaus

26
이 같은 I,하지만 당신은 기본 열거 형의 상한에 도착하면, 컴파일러는 다음과 같은 비트의 변화에 대해 경고하지 않습니다 1 << 31 == -2147483648, 1 << 32 == 1, 1 << 33 == 2, 등. 반대로 ThirtySecond = 2147483648int 유형 열거 형 을 말하면 컴파일러에서 오류가 발생합니다.
aaaantoine 2018 년

115

답변 https://stackoverflow.com/a/8462/1037948 (비트 시프 팅을 통한 선언) 및 https://stackoverflow.com/a/9117/1037948 (선언에서 조합 사용)을 사용하면 이전 값 을 비트 시프 팅 할 수 있습니다 숫자를 사용하는 것보다 반드시 권장하지는 않지만 단지 지적 할 수 있습니다.

오히려 :

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

선언 할 수 있습니다

[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로 확인 :

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

결과 :

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

23
조합은 좋은 권장 사항이지만 체인 된 비트 시프트는 Two = One << 1, Three = One << 1 등과 같은 복사 및 붙여 넣기 오류가 발생하기 쉽다고 생각합니다. 형식 1 << n이 더 안전하고 의도가 더 명확합니다.
Rupert Rawnsley

2
@RupertRawnsley 내 대답을 인용 :> 반드시 그것을 추천 할 필요는 없지만, 단지 당신을 지적
drzaus

49

선언 및 잠재적 사용법을 보여주는 예는 다음을 참조하십시오.

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

이 예제는 [Flags]를 생략하더라도 작동합니다. 내가 배우려고하는 [Flags] 부분입니다.
gbarry

39

C # 7에서 허용 된 답변 외에도 enum 플래그는 이진 리터럴을 사용하여 작성할 수 있습니다.

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

이 표현을 통해 표지 아래에서 플래그가 어떻게 작동하는지 명확하게 생각합니다 .


2
그리고 C # 7.2에서는 선행 구분 기호가 더 명확합니다 ! 0b_0100
Vincenzo Petronio

37

나는 최근 에 비슷한 것에 대해 물었다 .

플래그를 사용하는 경우 포함 된 플래그를 쉽게 확인할 수 있도록 확장 메소드를 열거 형에 추가 할 수 있습니다 (자세한 내용은 포스트 참조).

이를 통해 다음을 수행 할 수 있습니다.

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

그럼 당신은 할 수 있습니다 :

PossibleOptions opt = PossibleOptions.OptionOneTwoAndThree 

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

포함 된 플래그를 확인하는 대부분의 방법보다 읽기 쉽습니다.


IsSet은 내가 생각하는 확장 방법입니까?
Robert MacLean 2016 년

예-자세한 내용은 링크 된 다른 질문을 읽으십시오. stackoverflow.com/questions/7244
Keith

71
.NET 4는 HasFlag열거 형에 메소드를 추가 하므로 opt.HasFlag( PossibleOptions.OptionOne )고유 한 확장명을 작성 하지 않고도 할 수 있습니다.
Orion Edwards

1
참고 HasFlag이다 훨씬 느린 비트 연산을하는 것보다.
Wai Ha Lee

1
@WaiHaLee Expression을 사용하여 빠른 버전으로 컴파일 된 CodeJam.EnumHelper.IsFlagSet 확장 메서드를 사용할 수 있습니다. github.com/rsdn/CodeJam/wiki/M_CodeJam_EnumHelper_IsFlagSet__1
NN_

22

N

기존 값 세트에 다른 플래그를 추가하려면 OR 대입 연산자를 사용하십시오.

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

18

플래그로 작업 할 때 종종 추가 없음 및 모든 항목을 선언합니다. 모든 플래그가 설정되어 있는지 또는 플래그가 설정되어 있지 않은지 확인하는 데 도움이됩니다.

[Flags] 
enum SuitsFlags { 

    None =     0,

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

    All =      ~(~0 << 4)

}

용법:

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

 
2019-10 업데이트 :

C # 7.0부터는 이진 리터럴을 사용할 수 있는데, 이는 아마도 더 직관적입니다.

[Flags] 
enum SuitsFlags { 

    None =     0b0000,

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

    All =      0b1111

}


14

[정보 지나치게 자세한 나에게 뭔가있다 if ((x & y) == y)...구조는 특히, xy플래그의 두 화합물 세트이고, 당신은이 있는지 알고 싶은 모든 중복.

이 경우 실제로 알아야 할 것은 bitmasked 후 0이 아닌 값 [1]이 있는지 여부 입니다.

[1] Jaime의 의견을 참조하십시오. 우리가 진정으로 비트 마스킹 했다면 결과가 긍정적인지 확인하기 만하면 됩니다. 하지만 이후 enum의 부정적 일 수와 결합 할 때, 심지어 이상하게, 속성 , 그것은을위한 코드 방어의 것이 아니라 .[Flags]!= 0> 0

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

열거 형은 부호있는 유형을 기반으로 할 수 있으므로 "> 0"대신 "! = 0"을 사용해야합니다.
raven

@JaimePardos-우리가 정직한 바이트를 유지하는 한,이 예제에서와 같이 부정적인 개념은 없습니다. MSDN은 경고합니다 . "음수가 숫자를 플래그 열거 상수로 정의 할 경우주의해야합니다 . 그 이유는 코드가 혼동되고 코딩 오류가 발생할 수 있기 때문입니다." "네거티브 비트 플래그"의 관점에서 생각하는 것은 이상합니다! ; ^) 조금 더 편집하겠습니다. 하지만,에 음수 값을 사용하는 경우을 enum확인해야합니다 != 0.
ruffin

10

플래그를 사용하면 열거 내에서 비트 마스킹을 사용할 수 있습니다. 이를 통해 열거 된 값을 유지하면서 지정된 값을 유지할 수 있습니다.

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

0

누군가이 시나리오를 이미 알고 있다면 사과합니다. 우리가 볼 수있는 깃발의 완벽한 예. 예 바인딩 플래그 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

용법

             // 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 [] {});
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.