about_Enum

about_Enum (PowerShell 열거형)

열거형, 그러니까 enum을 활용하면 코드 안에서 이름 붙은 상수들의 집합을 깔끔하게 묶어서 쓸 수 있어요. 매직 넘버 대신 의미 있는 라벨로 값을 다루고, 잘못된 값을 실수로 넣는 일도 미리 막아주는 아주 유용한 문법이죠. 이 문서에서는 enum 선언부터 깃발(flag)로 쓰는 방법, 주요 메서드, 한계점까지 차근차근 살펴볼게요. 코드는 그대로 두고 설명만 쉽게 풀었으니 궁금한 부분만 골라 봐도 좋아요.

출처: Microsoft PowerShell 공식 문서 - about_Enum

본문

간단한 설명

enum 문은 열거형(enumeration)을 선언해요. 열거형은 열거 목록(enumerator list)이라 부르는 이름 붙은 라벨들의 집합으로 이루어진 뚜렷한 형식이에요.

자세한 설명

enum 문을 쓰면 강력한 형식의 라벨 집합을 만들 수 있어요. 이 열거형을 코드에서 그대로 사용하면, 값을 일일이 파싱하거나 오타가 있는지 검사할 필요가 없어져요.

열거형은 내부적으로 0부터 시작하는 정수 계열 값 형식으로 표현돼요. 기본적으로 PowerShell 열거형은 System.Int32([int])를 기반 형식으로 사용해요. 또 기본적으로 목록의 첫 번째 라벨에 값 0을 부여하고, 나머지 라벨에는 1씩 증가하는 연속된 정수를 부여해요.

구문

정의할 때 라벨에 아무 정수 값이나 줄 수 있어요. 값을 지정하지 않은 라벨은 다음 정수 값을 자동으로 받아요.

Enum 라벨에는 문자, 밑줄, 숫자만 넣을 수 있고, 숫자로 시작하면 안 돼요. 라벨은 따옴표로 감싼 문자열이 될 수 없으며, 반드시 붙임표 없는(bareword) 문자열로 해석되어야 해요. 라벨은 문자열로 해석되지 키워드로 해석되지 않아요. 그래서 언어 키워드와 같은 이름의 라벨(예: return)을 만드는 것도 가능해요.

열거형은 다음 구문들로 정의할 수 있어요.

  • 정수 열거형 정의 구문

    [[<attribute>]...] enum <enum-name> {
        <label> [= <int-value>]
        ...
    }
    
  • 특정 기반 형식 열거형 정의 구문

    [[<attribute>]...] enum <enum-name> : <underlying-type-name> {
        <label> [= <int-value>]
        ...
    }
    
  • 깃발(flag) 열거형 정의 구문

    [[<attribute>]...] [Flags()] enum <enum-name>[ : <underlying-type-name>] {
        <label 0> [= 1]
        <label 1> [= 2]
        <label 2> [= 4]
        <label 3> [= 8]
        ...
        ...
    }
    
  • 열거형 접근 구문

    [<enum-name>]::<label>
    

예제

예제 1 - 최소한의 열거형

아래 코드 블록은 세 개의 라벨을 가진 MarkdownUnorderedListCharacter 열거형을 정의해요. 어떤 라벨에도 명시적인 값을 지정하지 않았어요.

enum MarkdownUnorderedListCharacter {
    Asterisk
    Dash
    Plus
}

다음 코드 블록은 정수 값과 문자열 값이 각각 열거형 형식으로 캐스팅될 때 어떻게 동작하는지 보여줘요.

$ValuesToConvert = @(0, 'Asterisk', 1, 'Dash', 2, 'Plus')
foreach ($Value in $ValuesToConvert) {
    [MarkdownUnorderedListCharacter]$EnumValue = $Value

    [pscustomobject]@{
        AssignedValue = $Value
        Enumeration   = $EnumValue
        AreEqual      = $Value -eq $EnumValue
    }
}
AssignedValue Enumeration AreEqual
------------- ----------- --------
            0    Asterisk     True
     Asterisk    Asterisk     True
            1        Dash     True
         Dash        Dash     True
            2        Plus     True
         Plus        Plus     True

열거형의 값과 같은 정수를 캐스팅하면 해당 열거형이 나와요. 열거형의 라벨과 같은 문자열을 캐스팅해도 해당 열거형이 나와요.

예제 2 - 명시적 값과 동의어(synonym) 값

아래 예제는 미디어 파일에 대응하는 개체들의 열거형을 보여줘요. 정의에서는 music, picture, video의 기반 값에 명시적인 값을 지정했어요. 명시적 할당 바로 뒤에 오는 라벨들은 다음 정수 값을 받아요. 같은 값을 다른 라벨에 다시 지정하면 동의어를 만들 수 있어요. ogg, oga, moggjpg, jpeg, 또는 mpg, mpeg처럼 만들어진 값을 확인해 보세요.

enum MediaTypes {
    unknown
    music   = 10
    mp3
    aac
    ogg     = 15
    oga     = 15
    mogg    = 15
    picture = 20
    jpg
    jpeg    = 21
    png
    video   = 40
    mpg
    mpeg    = 41
    avi
    m4v
}

GetEnumNames() 메서드는 열거형의 라벨 목록을 반환해요.

[MediaTypes].GetEnumNames()
unknown
music
mp3
aac
ogg
oga
mogg
picture
jpg
jpeg
png
video
mpg
mpeg
avi
m4v

GetEnumValues() 메서드는 열거형의 값 목록을 반환해요.

[MediaTypes].GetEnumValues()
unknown
music
mp3
aac
ogg
ogg
ogg
picture
jpg
jpg
png
video
mpg
mpg
avi
m4v

참고

GetEnumNames()GetEnumValues()는 겉보기에 같은 결과, 즉 이름 붙은 값들의 목록을 반환하는 것처럼 보여요. 하지만 내부적으로 GetEnumValues()는 값을 나열한 뒤 그 값을 이름에 매핑해요. 결과를 잘 살펴보면 GetEnumNames() 출력에는 ogg, oga, mogg가 모두 나타나지만 GetEnumValues() 출력에는 ogg만 보여요. jpg, jpegmpg, mpeg에서도 똑같은 일이 일어나요. 동의어 값에 대해 PowerShell이 반환하는 이름은 결정적이지 않아요.

GetEnumName() 메서드를 쓰면 특정 값에 연결된 이름을 얻을 수 있어요. 값에 연결된 이름이 여러 개라면 이 메서드는 가장 먼저 정의된 이름을 반환해요.

[MediaTypes].GetEnumName(15)
ogg

아래 예제는 각 이름을 그 값에 매핑하는 방법을 보여줘요.

[MediaTypes].GetEnumNames() | ForEach-Object {
  [pscustomobject]@{
    Name = $_
    Value = [int]([MediaTypes]::$_)
  }
}
Name    Value
----    -----
unknown     0
music      10
mp3        11
aac        12
ogg        15
oga        15
mogg       15
picture    20
jpg        21
jpeg       21
png        22
video      40
mpg        41
mpeg       41
avi        42
m4v        43

[<enum-name>]::<label> 구문을 쓰면 라벨 하나로 단일 열거형 값을 지정할 수 있어요.

[MediaTypes]::png
[MediaTypes]::png -eq 22
png
True

예제 3 - 깃발로 쓰는 열거형

아래 코드 블록은 FileAttributes 열거형을 비트 깃발의 집합으로 만들어요. 각 라벨의 값은 이전 라벨 값의 두 배예요.

[Flags()] enum FileAttributes {
    Archive    = 1
    Compressed = 2
    Device     = 4
    Directory  = 8
    Encrypted  = 16
    Hidden     = 32
}

[FileAttributes]$file1 =  [FileAttributes]::Archive
[FileAttributes]$file1 += [FileAttributes]::Compressed
[FileAttributes]$file1 += [FileAttributes]::Device
"file1 attributes are: $file1"

[FileAttributes]$file2 = [FileAttributes]28 ## => 16 + 8 + 4
"file2 attributes are: $file2"
file1 attributes are: Archive, Compressed, Device
file2 attributes are: Device, Directory, Encrypted

특정 깃발이 설정되어 있는지 확인하려면 이진 비교 연산자 -band를 쓰면 돼요. 이 예제는 $file2 값에서 Device 특성과 Archive 특성을 검사해요.

PS > ($file2 -band [FileAttributes]::Device) -eq [FileAttributes]::Device
True

PS > ($file2 -band [FileAttributes]::Archive) -eq [FileAttributes]::Archive
False

HasFlag() 메서드를 써서도 특정 깃발이 설정됐는지 확인할 수 있어요. 이 예제는 $file1 값에서 Device 특성과 Hidden 특성을 검사해요.

PS > $file1.HasFlag([FileAttributes]::Device)
True

PS > $file1.HasFlag([FileAttributes]::Hidden)
False

예제 4 - 매개 변수로 쓰는 열거형

아래 예제에서 ConvertTo-LineEndingRegex 함수는 형식이 EndOfLineInputObject 매개 변수를 정의해요.

enum EndOfLine {
    CR   = 1
    LF   = 2
    CRLF = 3
}

function ConvertTo-LineEndingRegex {
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline)]
        [EndOfLine[]]$InputObject
    )

    process {
        switch ($InputObject) {
            CR   {  '\r'  }
            LF   {  '\n'  }
            CRLF { '\r\n' }
        }
    }
}

[EndOfLine]::CR | ConvertTo-LineEndingRegex

'CRLF' | ConvertTo-LineEndingRegex

ConvertTo-LineEndingRegex 2
\r

\r\n

\n

예제에서 ConvertTo-LineEndingRegex를 호출하는 첫 번째 문은 CR의 열거형 값을 전달해요. 두 번째 문은 'CRLF' 문자열을 전달하되, 이것이 LineEnding으로 캐스팅돼요. 세 번째 문은 매개 변수에 값 2를 지정하며, 이것은 LF 라벨에 매핑돼요.

PowerShell 프롬프트에 다음 텍스트를 입력하면 인수 완성(argument completion) 옵션을 볼 수 있어요.

ConvertTo-LineEndingRegex -InputObject <Tab>

매개 변수에 잘못된 라벨 이름이나 숫자 값을 지정하면 함수가 오류를 발생시켜요.

ConvertTo-LineEndingRegex -InputObject 0
ConvertTo-LineEndingRegex: Cannot process argument transformation on
parameter 'InputObject'. Cannot convert value "0" to type "EndOfLine" due
to enumeration values that are not valid. Specify one of the following
enumeration values and try again. The possible enumeration values are
"CR,LF,CRLF".

예제 5 - 특정 기반 형식을 가진 열거형

PowerShell 6.2부터 열거형을 특정 기반 형식으로 정의할 수 있어요. 이 예제는 열거형에 유효한 기반 형식들을 보여줘요.

첫 번째 코드 블록은 두 개의 변수를 배열로 초기화해요. $EnumTypes는 동적으로 생성된 형식을 담을 빈 배열이고, $IntegralTypes는 열거형의 유효한 기반 형식을 담은 배열이에요.

$EnumTypes     = @()
$IntegralTypes = @(
    'byte', 'sbyte', 'short', 'ushort', 'int', 'uint', 'long', 'ulong'
)

다음 코드 블록은 열거형 정의를 동적으로 만들 때 쓸 템플릿을 정의해요. {0} 형식 자리 표시자를 정수 형식 이름으로 바꾸면, 템플릿이 다음 작업을 하는 스크립트 블록을 만들어요.

  1. byteEnum처럼 <type>Enum이라는 이름의 열거형을 정의해요. 정의된 열거형은 지정한 정수 형식을 기반 값 형식으로 사용해요.

    이 열거형은 정수 형식의 최솟값을 갖는 Min 값과 최댓값을 갖는 Max 값을 정의해요.

  2. 새로 정의된 형식을 반환해요.

$DefinitionTemplate = @"
enum {0}Enum : {0} {
    Min = [{0}]::MinValue
    Max = [{0}]::MaxValue
}

[{0}Enum]
"@

다음 코드 블록은 템플릿을 사용해 현재 범위에서 스크립트 블록을 만들고 호출해요. 반환된 형식 정의를 $EnumTypes 배열에 추가해요.

foreach ($IntegralType in $IntegralTypes) {
    $Definition  = $DefinitionTemplate -f $IntegralType
    $ScriptBlock = [scriptblock]::Create($Definition)
    $EnumTypes  += . $ScriptBlock
}

마지막 코드 블록은 열거형 형식을 반복하면서 GetEnumValuesAsUnderlyingType() 메서드로 값을 기반 형식으로 나열해요. 각 값마다 새 개체를 만들어 열거형 형식, 값 형식, 라벨, 실제 값을 보여줘요.

foreach ($EnumType in $EnumTypes) {
    $EnumType.GetEnumValuesAsUnderlyingType() | ForEach-Object {
        [pscustomobject]@{
            EnumType  = $EnumType.FullName
            ValueType = $_.GetType().FullName
            Label     = $EnumType.GetEnumName($_)
            Value     = $_
        }
    }
}
EnumType   ValueType     Label                Value
--------   ---------     -----                -----
byteEnum   System.Byte   Min                      0
byteEnum   System.Byte   Max                    255
sbyteEnum  System.SByte  Max                    127
sbyteEnum  System.SByte  Min                   -128
shortEnum  System.Int16  Max                  32767
shortEnum  System.Int16  Min                 -32768
ushortEnum System.UInt16 Min                      0
ushortEnum System.UInt16 Max                  65535
intEnum    System.Int32  Max             2147483647
intEnum    System.Int32  Min            -2147483648
uintEnum   System.UInt32 Min                      0
uintEnum   System.UInt32 Max             4294967295
longEnum   System.Int64  Max    9223372036854775807
longEnum   System.Int64  Min   -9223372036854775808
ulongEnum  System.UInt64 Min                      0
ulongEnum  System.UInt64 Max   18446744073709551615

열거형 메서드

PowerShell에서 열거형에 쓸 수 있는 유용한 메서드들과 그 사용법을 정리한 목록이에요.

Format

Format() 정적 메서드는 주어진 열거형 형식, 열거형 값, 형식 문자열에 대해 형식화된 문자열 출력을 반환해요. 출력은 지정한 형식 문자열로 값에 ToString 메서드를 호출한 결과와 같아요.

이 정적 메서드는 System.Enum 기본 클래스 형식이나 특정 열거형 형식에서 쓸 수 있어요.

[System.Enum]::Format([<enum-name>], <value>, <format-string>)
[<enum-name>]::Format([<enum-name>], <value>, <format-string>)

유효한 형식 문자열은 G 또는 g, D 또는 d, X 또는 x, F 또는 f예요. 자세한 내용은 열거형 형식 문자열을 참고하세요.

아래 예제는 지원되는 열거형 형식 문자열을 각각 사용해서 TaskState 열거형의 각 값을 문자열 표현으로 변환해요.

enum TaskState {
    ToDo
    Doing
    Done
}

# String format template for the statements
$Statement = "[System.Enum]::Format([TaskState], {0}, '{1}')"

foreach ($Format in @('G', 'D', 'X', 'F')) {
    $StatementToDo  = $Statement -f 0, $Format
    $StatementDoing = $Statement -f "([TaskState]'Doing')", $Format
    $StatementDone  = $Statement -f '[TaskState]::Done', $Format
    $FormattedToDo  = [System.Enum]::Format(
      [TaskState], 0, $Format
    )
    $FormattedDoing = [System.Enum]::Format(
        [TaskState], ([TaskState]'Doing'), $Format
    )
    $FormattedDone  = [System.Enum]::Format(
      [TaskState], [TaskState]::Done, $Format
    )

    "{0,-62} => {1}" -f $StatementToDo,  $FormattedToDo
    "{0,-62} => {1}" -f $StatementDoing, $FormattedDoing
    "{0,-62} => {1}" -f $StatementDone,  $FormattedDone
}
[System.Enum]::Format([TaskState], 0, 'G')                     => ToDo
[System.Enum]::Format([TaskState], ([TaskState]'Doing'), 'G')  => Doing
[System.Enum]::Format([TaskState], [TaskState]::Done, 'G')     => Done
[System.Enum]::Format([TaskState], 0, 'D')                     => 0
[System.Enum]::Format([TaskState], ([TaskState]'Doing'), 'D')  => 1
[System.Enum]::Format([TaskState], [TaskState]::Done, 'D')     => 2
[System.Enum]::Format([TaskState], 0, 'X')                     => 00000000
[System.Enum]::Format([TaskState], ([TaskState]'Doing'), 'X')  => 00000001
[System.Enum]::Format([TaskState], [TaskState]::Done, 'X')     => 00000002
[System.Enum]::Format([TaskState], 0, 'F')                     => ToDo
[System.Enum]::Format([TaskState], ([TaskState]'Doing'), 'F')  => Doing
[System.Enum]::Format([TaskState], [TaskState]::Done, 'F')     => Done

GetEnumName

GetEnumName() 리플렉션 메서드는 특정 열거형 값의 이름을 반환해요. 입력 값은 정수 같은 열거형의 유효한 기반 형식이거나 열거형 값이어야 해요. 값에 연결된 이름이 여러 개라면 이 메서드는 가장 먼저 정의된 이름을 반환해요.

[<enum-name>].GetEnumName(<value>)
enum GateState {
    Unknown
    Open
    Opening
    Closing
    Closed
}

foreach ($Value in 0..4) {
    [pscustomobject]@{
      IntegerValue = $Value
      EnumName     = [GateState].GetEnumName($Value)
    }
}
IntegerValue EnumName
------------ --------
           0 Unknown
           1 Open
           2 Opening
           3 Closing
           4 Closed

GetEnumNames

GetEnumNames() 리플렉션 메서드는 모든 열거형 값의 이름을 문자열로 반환해요. 출력에는 동의어도 포함돼요.

[<enum-name>].GetEnumNames()
enum Season {
    Unknown
    Spring
    Summer
    Autumn
    Winter
    Fall   = 3
}

[Season].GetEnumNames()
Unknown
Spring
Summer
Fall
Autumn
Winter

GetEnumUnderlyingType

GetEnumUnderlyingType() 리플렉션 메서드는 열거형 값의 기반 형식을 반환해요.

[<enum-name>].GetEnumUnderlyingType()
enum IntBasedEnum {
    Zero
    One
    Two
}
enum ShortBasedEnum : short {
    Zero
    One
    Two
}

foreach ($EnumType in @([IntBasedEnum], [ShortBasedEnum])) {
    [pscustomobject]@{
        EnumType = $EnumType
        ValueType = $EnumType.GetEnumUnderlyingType()
    }
}
EnumType       ValueType
--------       ---------
IntBasedEnum   System.Int32
ShortBasedEnum System.Int16

GetEnumValues

GetEnumValues() 리플렉션 메서드는 열거형에 정의된 모든 값을 반환해요.

[<enum-name>].GetEnumValues()
enum Season {
    Unknown
    Spring
    Summer
    Autumn
    Winter
    Fall   = 3
}

[Season].GetEnumValues()
Unknown
Spring
Summer
Fall
Fall
Winter

GetEnumValuesAsUnderlyingType

GetEnumValuesAsUnderlyingType() 리플렉션 메서드는 열거형에 정의된 모든 값을 기반 형식으로 반환해요.

[<enum-name>].GetEnumValuesAsUnderlyingType()
enum IntBasedEnum {
    Zero
    One
    Two
}
enum ShortBasedEnum : short {
    Zero
    One
    Two
}

foreach ($EnumType in @([IntBasedEnum], [ShortBasedEnum])) {
    [pscustomobject]@{
        EnumType = $EnumType
        ValueType = $EnumType.GetEnumValuesAsUnderlyingType()[0].GetType()
    }
}
EnumType       ValueType
--------       ---------
IntBasedEnum   System.Int32
ShortBasedEnum System.Int16

HasFlag

HasFlag 인스턴스 메서드는 깃발 열거형 값에 특정 비트 깃발이 설정돼 있는지 판단해요. 이 메서드는 이진 비교와 동등 확인을 하는 것보다 짧고 읽기 쉬워요.

<enum-value>.HasFlag(<enum-flag-value>)

아래 예제는 ModuleFeatures 깃발 열거형을 정의하고 값 39가 어떤 깃발들을 갖는지 보여줘요.

[Flags()] enum ModuleFeatures {
    Commands  = 1
    Classes   = 2
    Enums     = 4
    Types     = 8
    Formats   = 16
    Variables = 32
}

$Features = [ModuleFeatures]39

foreach ($Feature in [ModuleFeatures].GetEnumValues()) {
    "Has flag {0,-12}: {1}" -f "'$Feature'", ($Features.HasFlag($Feature))
}
Has flag 'Commands'  : True
Has flag 'Classes'   : True
Has flag 'Enums'     : True
Has flag 'Types'     : False
Has flag 'Formats'   : False
Has flag 'Variables' : True

IsDefined

IsDefined() 정적 메서드는 입력 값이 열거형에 정의돼 있으면 $true를, 그렇지 않으면 $false를 반환해요. 잘못된 인자 오류를 처리할 필요 없이 값이 열거형에 유효한지 확인할 때 이 메서드를 쓰면 좋아요.

이 정적 메서드는 System.Enum 기본 클래스 형식이나 특정 열거형 형식에서 쓸 수 있어요.

[System.Enum]::IsDefined([<enum-name>], <value>)
[<enum-name>]::IsDefined([<enum-name>], <value>)
enum Season {
    Unknown
    Spring
    Summer
    Autumn
    Winter
    Fall   = 3
}

foreach ($Value in 0..5) {
    $IsValid   = [Season]::IsDefined([Season], $Value)
    $EnumValue = if ($IsValid) { [Season]$Value }

    [pscustomobject] @{
        InputValue = $Value
        IsValid    = $IsValid
        EnumValue  = $EnumValue
    }
}
InputValue IsValid EnumValue
---------- ------- ---------
         0    True   Unknown
         1    True    Spring
         2    True    Summer
         3    True      Fall
         4    True    Winter
         5   False

ToString

ToString() 인스턴스 메서드는 열거형 값의 라벨을 반환해요. 이 메서드는 열거형 값이 출력으로 표시될 때의 기본 보기이기도 해요. 선택적으로 형식 문자열을 지정해서 값이 표시되는 방식을 제어할 수도 있어요. 형식 지정에 관한 자세한 내용은 열거형 값 형식 지정을 참고하세요.

참고

특정 값에 대해 동의어를 정의한 열거형이라면 ToString() 출력에 의존하는 코드를 작성하지 마세요. 이 메서드는 그 값에 대한 아무 유효한 이름이나 반환할 수 있어요.

<enum-value>.ToString([<format-string>])

아래 예제는 Grey의 동의어로 Gray를 가진 Shade 열거형을 정의해요. 그런 다음 실제 열거형 값, 열거형 문자열, 열거형 정수를 보여주는 개체들을 출력해요.

enum Shade {
    White
    Grey
    Gray = 1
    Black
}

[Shade].GetEnumValues() | ForEach-Object -Process {
    [pscustomobject]@{
        EnumValue    = $_
        StringValue  = $_.ToString()
        IntegerValue = [int]$_
    }
}
EnumValue StringValue IntegerValue
--------- ----------- ------------
    White White                  0
     Grey Grey                   1
     Grey Grey                   1
    Black Black                  2

열거형 값의 동의어

같은 정수 값에 다른 이름을 부여하는 열거형을 정의할 수 있어요. 이렇게 하면 같은 기반 값을 가리키는 이름들을 동의어라고 불러요. 동의어가 있는 열거형은 사용자가 같은 값에 대해 서로 다른 이름을 지정할 수 있게 해줘요.

동의어가 있는 열거형을 정의할 때는 동의어 값이 특정 이름으로 변환된다는 전제의 코드를 작성하지 마세요. 동의어 문자열을 열거형 값으로 변환하는 코드는 안정적으로 작성할 수 있어요. 열거형 값 자체를 다룰 때는 문자열이 아니라 항상 열거형 값이나 그 기반 형식으로 비교하세요.

아래 코드 블록은 GreyGray를 동의어로 가진 Shade 열거형을 정의해요.

enum Shade {
    White
    Grey
    Gray = 1
    Black
}

[Shade]'Grey' -eq [Shade]::Gray
[Shade]::Grey -eq 1
[Shade]'Gray' -eq 1
True
True
True

깃발로 쓰는 열거형

열거형의 흔한 용도 중 하나는 상호 배타적인 값들의 집합을 표현하는 거예요. 예를 들어 ArrivalStatus 인스턴스는 Early, OnTime, Late 중 하나의 값을 가질 수 있어요. ArrivalStatus 인스턴스의 값이 둘 이상의 열거형 상수를 반영한다는 건 말이 안 되죠.

하지만 다른 경우에는 열거형 개체의 값에 여러 열거형 멤버가 포함될 수 있고, 각 멤버는 열거형 값 안의 비트 필드를 나타내요. FlagsAttribute를 사용하면 열거형이 사용자가 조합할 수 있는 비트 필드 깃발들로 구성됨을 나타낼 수 있어요.

깃발로 쓰는 열거형이 제대로 동작하려면 각 라벨의 정수 값을 2의 거듭제곱으로 설정해야 해요. 라벨 값을 지정하지 않으면 PowerShell은 이전 라벨보다 1 큰 값으로 설정해요.

흔히 쓰는 깃발 조합에 이름을 달아서 정의할 수도 있어요. 이렇게 하면 사용자가 깃발 집합을 한 번에 더 쉽게 지정할 수 있어요. 그 값의 이름은 깃발들의 이름을 합친 형태여야 하고, 정수 값은 깃발 값들의 합이어야 해요.

값에 특정 깃발이 설정됐는지 확인하려면 값의 HasFlag() 메서드를 쓰거나 이진 비교 연산자 -band를 쓰면 돼요.

깃발 열거형을 사용하고 깃발이 설정됐는지 확인하는 예시는 예제 3을 참고하세요.

매개 변수로 쓰는 열거형

형식이 enum인 cmdlet 매개 변수를 정의할 수 있어요. 매개 변수의 형식을 enum으로 지정하면 사용자는 매개 변수 값에 대한 자동 완성과 유효성 검사를 받게 돼요. 인수 완성은 enum의 유효한 라벨 목록을 제시해줘요.

매개 변수의 형식이 enum일 때는 다음 중 어떤 것이든 지정할 수 있어요.

  • [<EnumType>]::<Label> 같은 열거형
  • 라벨을 문자열로 지정한 열거형
  • 열거형의 숫자 값

열거형 형식 매개 변수의 동작을 보여주는 예시는 예제 4를 참고하세요.

특정 기반 형식을 가진 열거형

PowerShell 6.2부터 열거형을 특정 기반 형식으로 정의할 수 있어요. 기반 형식을 지정하지 않고 열거형을 정의하면 PowerShell은 [int](System.Int32)를 기반 형식으로 만든 열거형을 생성해요.

열거형의 기반 형식은 정수 계열 숫자 형식이어야 해요. 다음 목록은 짧은 이름과 전체 형식 이름을 함께 가진 유효한 형식들이에요.

  • byte - System.Byte
  • sbyte - System.SByte
  • short - System.Int16
  • ushort - System.UInt16
  • int - System.Int32
  • uint - System.UInt32
  • long - System.Int64
  • ulong - System.UInt64

열거형의 기반 형식은 짧은 이름이나 전체 형식 이름 중 어느 것으로든 지정할 수 있어요. 아래 정의들은 기능적으로 동일해요. 기반 형식에 쓰는 이름만 다를 뿐이에요.

enum LongValueEnum : long {
    Zero
    One
    Two
}
enum LongValueEnum : System.Int64 {
    Zero
    One
    Two
}

열거형 값 형식 지정

정적 Format 메서드와 인스턴스 ToString 메서드의 오버로드를 호출하면 열거형 값을 문자열 표현으로 변환할 수 있어요. 형식 문자열을 사용하면 열거형 값이 문자열로 표현되는 정확한 방식을 제어할 수 있어요. 자세한 내용은 열거형 형식 문자열을 참고하세요.

아래 예제는 지원되는 열거형 형식 문자열(G 또는 g, D 또는 d, X 또는 x, F 또는 f)을 각각 사용해서 TaskState 열거형의 각 멤버를 문자열 표현으로 변환해요.

enum TaskState {
    ToDo
    Doing
    Done
}

[TaskState].GetEnumValues() | ForEach-Object {
    [pscustomobject]@{
        "ToString('G')" = $_.ToString('G')
        "ToString('D')" = $_.ToString('D')
        "ToString('X')" = $_.ToString('X')
        "ToString('F')" = $_.ToString('F')
    }
}
ToString('G') ToString('D') ToString('X') ToString('F')
------------- ------------- ------------- -------------
ToDo          0             00000000      ToDo
Doing         1             00000001      Doing
Done          2             00000002      Done

아래 예제는 깃발 열거형의 값들에 형식 문자열을 사용해요.

[Flags()] enum FlagEnum {
    A = 1
    B = 2
    C = 4
}

$FlagValues = @(
    [FlagEnum]::A                                 # 1
    [FlagEnum]::B                                 # 2
    [FlagEnum]::A + [FlagEnum]::B                 # 3
    [FlagEnum]::C                                 # 4
    [FlagEnum]::C + [FlagEnum]::A                 # 5
    [FlagEnum]::C + [FlagEnum]::B                 # 6
    [FlagEnum]::C + [FlagEnum]::A + [FlagEnum]::B # 7
    [FlagEnum]::C + [FlagEnum]::C                 # 8
)

foreach ($Value in $FlagValues) {
    [pscustomobject]@{
        "ToString('G')" = $Value.ToString('G')
        "ToString('D')" = $Value.ToString('D')
        "ToString('X')" = $Value.ToString('X')
        "ToString('F')" = $Value.ToString('F')
    }
}
ToString('G') ToString('D') ToString('X') ToString('F')
------------- ------------- ------------- -------------
A             1             00000001      A
B             2             00000002      B
A, B          3             00000003      A, B
C             4             00000004      C
A, C          5             00000005      A, C
B, C          6             00000006      B, C
A, B, C       7             00000007      A, B, C
8             8             00000008      8

깃발 열거형에서는 GF 형식 문자열이 값에 설정된 깃발 목록을 쉼표로 구분해서 표시한다는 점을 확인해 보세요. 마지막 값 8은 실제로 유효한 깃발 집합이 아니므로 어떤 깃발도 나열하지 않아요. 적어도 하나의 깃발을 중복하지 않고서는 열거형 깃발을 합쳐서 8을 만들 수 없어요.

Update-TypeData로 확장 메서드 정의하기

열거형의 선언 안에서는 메서드를 정의할 수 없어요. 열거형의 기능을 확장하려면 Update-TypeData cmdlet을 사용해서 열거형에 ScriptMethod 멤버를 정의하면 돼요.

아래 예제는 Update-TypeData cmdlet을 사용해 FileAttributes 깃발 열거형에 GetFlags() 메서드를 추가해요. 이 메서드는 값에 설정된 깃발들의 배열을 반환해요.

[Flags()] enum FileAttributes {
    Archive    = 1
    Compressed = 2
    Device     = 4
    Directory  = 8
    Encrypted  = 16
    Hidden     = 32
}

$MemberDefinition = @{
    TypeName   = 'FileAttributes'
    MemberName = 'GetFlags'
    MemberType = 'ScriptMethod'
    Value      = {
        foreach ($Flag in $this.GetType().GetEnumValues()) {
          if ($this.HasFlag($Flag)) { $Flag }
        }
    }
}

Update-TypeData @MemberDefinition

$File = [FileAttributes]28

$File.GetFlags()
Device
Directory
Encrypted

형식 가속기를 이용한 열거형 내보내기

기본적으로 PowerShell 모듈은 PowerShell에서 정의된 클래스와 열거형을 자동으로 내보내지 않아요. 모듈 밖에서는 using module 문을 호출하지 않으면 사용자 지정 형식을 사용할 수 없어요.

하지만 모듈이 형식 가속기를 추가하면, 사용자가 모듈을 가져온 뒤에는 그 형식 가속기를 세션에서 즉시 사용할 수 있어요.

참고

세션에 형식 가속기를 추가하는 것은 내부(비공개) API를 사용해요. 이 API를 사용하면 충돌이 발생할 수 있어요. 아래에서 설명하는 패턴은 모듈을 가져올 때 같은 이름의 형식 가속기가 이미 존재하면 오류를 발생시켜요. 또한 세션에서 모듈을 제거할 때 형식 가속기도 함께 제거해요.

이 패턴은 형식들이 세션에서 사용 가능하도록 보장해요. 다만 VS Code에서 스크립트 파일을 작성할 때 IntelliSense나 완성에는 영향을 주지 않아요. VS Code에서 사용자 지정 형식에 대한 IntelliSense와 완성 제안을 받으려면 스크립트 맨 위에 using module 문을 추가해야 해요.

아래 패턴은 모듈에서 PowerShell 클래스와 열거형을 형식 가속기로 등록하는 방법을 보여줘요. 형식 정의 뒤에 있는 루트 스크립트 모듈에 이 스니펫을 추가하세요. $ExportableTypes 변수에 사용자가 모듈을 가져올 때 사용할 수 있게 만들 각 형식을 넣었는지 확인하세요. 나머지 코드는 수정할 필요가 없어요.

# Define the types to export with type accelerators.
$ExportableTypes =@(
    [DefinedTypeName]
)
# Get the internal TypeAccelerators class to use its static methods.
$TypeAcceleratorsClass = [psobject].Assembly.GetType(
    'System.Management.Automation.TypeAccelerators'
)
# Ensure none of the types would clobber an existing type accelerator.
# If a type accelerator with the same name exists, throw an exception.
$ExistingTypeAccelerators = $TypeAcceleratorsClass::Get
foreach ($Type in $ExportableTypes) {
    if ($Type.FullName -in $ExistingTypeAccelerators.Keys) {
        $Message = @(
            "Unable to register type accelerator '$($Type.FullName)'"
            'Accelerator already exists.'
        ) -join ' - '

        throw [System.Management.Automation.ErrorRecord]::new(
            [System.InvalidOperationException]::new($Message),
            'TypeAcceleratorAlreadyExists',
            [System.Management.Automation.ErrorCategory]::InvalidOperation,
            $Type.FullName
        )
    }
}
# Add type accelerators for every exportable type.
foreach ($Type in $ExportableTypes) {
    $TypeAcceleratorsClass::Add($Type.FullName, $Type)
}
# Remove type accelerators when the module is removed.
$MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = {
    foreach($Type in $ExportableTypes) {
        $TypeAcceleratorsClass::Remove($Type.FullName)
    }
}.GetNewClosure()

사용자가 모듈을 가져오면 세션의 형식 가속기에 추가된 모든 형식이 IntelliSense와 완성에 즉시 사용 가능해져요. 모듈이 제거되면 형식 가속기도 함께 제거돼요.

PowerShell 모듈에서 열거형 수동으로 가져오기

Import-Module#Requires 문은 모듈이 정의한 함수, 별칭, 변수만 가져와요. 열거형은 가져오지 않아요.

모듈이 클래스와 열거형을 정의했지만 그 형식들에 대한 형식 가속기를 추가하지 않았다면, using module 문을 사용해서 가져오세요.

using module 문은 스크립트 모듈 또는 이진 모듈의 루트 모듈(ModuleToProcess)에서 클래스와 열거형을 가져와요. 중첩 모듈에 정의된 클래스나 루트 모듈에 점 소싱된 스크립트에 정의된 클래스는 일관되게 가져오지 않아요. 모듈 밖의 사용자에게 사용 가능하게 만들고 싶은 클래스는 루트 모듈에 직접 정의하세요.

using 문에 대한 자세한 내용은 about_Using을 참고하세요.

개발 중 변경된 코드 불러오기

스크립트 모듈을 개발할 때는 코드를 바꾼 다음 Force 매개 변수와 함께 Import-Module을 사용해 모듈의 새 버전을 불러오는 일이 흔해요. 이 방식은 루트 모듈의 함수 변경에만 적용돼요. Import-Module은 중첩 모듈을 다시 불러오지 않아요. 또 업데이트된 클래스를 불러올 방법도 없어요.

최신 버전을 실행하고 있는지 확실히 하려면 새 세션을 시작해야 해요. PowerShell에서 정의되고 using 문으로 가져온 클래스와 열거형은 언로드할 수 없어요.

또 다른 흔한 개발 방식은 코드를 서로 다른 파일로 나누는 거예요. 한 파일의 함수가 다른 모듈에 정의된 열거형을 사용한다면, using module 문을 사용해서 함수가 필요한 열거형 정의를 갖도록 해야 해요.

한계점

  • PowerShell에서 정의한 열거형 값에는 특성을 붙일 수 없어요. 열거형을 비트 깃발 집합으로 정의하기 위한 FlagsAttribute처럼, 열거형 선언 자체에만 특성을 붙일 수 있어요.

    해결 방법: 없음

  • 열거형 정의 안에서는 메서드를 정의할 수 없고, PowerShell은 C#처럼 [확장 메서드]를 정의하는 것을 지원하지 않아요.

    해결 방법: Update-TypeData cmdlet을 사용해서 열거형에 ScriptMethod 멤버를 정의하세요.

더 알아보기