Enumerators have always been a part of Windows PowerShell, because they’re an important part of the .NET Framework. But they’re about to become even more popular, because changes in Windows PowerShell 5.0 make them very easy to create.
———–
Enumerators (“enums”) define a set of values, knows as “members” of the enumeration. You use enums to create a set of valid values and prohibit all other values. In Windows PowerShell, you can use enumerators as types, just as you use .NET classes. For example, the value of the ExecutionPolicy parameter of the Set-ExecutionPolicy cmdlet has a type of ExecutionPolicy and its Scope parameter has a type of ExecutionPolicyScope.
Set-ExecutionPolicy [-ExecutionPolicy] <ExecutionPolicy> [[-Scope] <ExecutionPolicyScope>] [-Force] [-WhatIf] [-Confirm] [<CommonParameters>]
Both ExecutionPolicy and ExecutionPolicyScope are enumerators. The enum members are listed and defined on the MSDN page for each enumerator.
If you try to use a value that is not a member of the enumeration, you get a very helpful error. The error explains that the value is limited to the enumeration and lists the valid values. So, a quick way to find the members of an enumeration is to try a value that is clearly not valid, like good old “foo”.
[ADMIN]: PS C:\ps-test> Set-ExecutionPolicy -ExecutionPolicy foo Set-ExecutionPolicy : Cannot bind parameter 'ExecutionPolicy'. Cannot convert value "foo" to type "Microsoft.PowerShell.ExecutionPolicy". Error: "Unable to match the identifier name foo to a valid enumerator name. Specify one of the following enumerator names and try again: Unrestricted, RemoteSigned, AllSigned, Restricted, Default, Bypass, Undefined" At line:1 char:38 + Set-ExecutionPolicy -ExecutionPolicy foo + ~~~ + CategoryInfo : InvalidArgument: (:) [Set-ExecutionPolicy], ParameterBindingException + FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.SetExecutionPolicyCommand
It’s easy to use enumerators. This excellent article from the TechNet Library explains it well. And, Windows PowerShell has let you define your own enumerators since 1.0, but it was a bit tricky. Not any more!
Create an enumerator
In Windows PowerShell 5.0, the enum keyword makes it much easier. Here’s a WineSweetness enumerator that I created for my Wine class.
enum WineSweetness { VeryDry Dry Moderate Sweet VerySweet } |
I use it to limit parameter values, much like ValidateSet, but unlike ValidateSet, once it’s defined in my session, script, or module, I can use it repeatedly without redefining it. In this example, I use it to limit the values of the Sweetness property of instances of my Wine class.
class Wine { #Properties [String]$Name [WineSweetness]$Sweetness ... } |
I can also use it functions, such as my Get-Wine function, without redefining it.
function Get-Wine { Param ( [parameter(Mandatory = $false)] [WineSweetness] $SweetnessValue, ... } |
Enum syntax
The enum syntax is so easy. Just note that the list is NOT comma-separated. If you prefer to list the values on a single line, use semi-colons.
enum <Name> { Value1 [Value2...] } |
-or-
enum <Name> { Value1; [Value2...;] } |
The value names cannot include spaces or special characters, although an underscore (_) is permitted. Enclosing a names with spaces in quotation marks or curly braces doesn’t help. If you include a space, Windows PowerShell thinks it’s an expression, which is permitted.
enum WineSweetness { VeryDry Dry Moderate Sweet Very Sweet # <--- No spaces } Very : The term 'Very' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. At line:1 char:5 + Very Sweet + ~~~~ + CategoryInfo : ObjectNotFound: (Very:String) [], CommandNotFoundException + FullyQualifiedErrorId : CommandNotFoundException
The values cannot be numbers and the first character of a value name cannot be a number. The ‘ missing closing “}” ‘ error is not very helpful, so watch out for this one.
PS C:\> enum WineSweetness { VeryDry Dry 3 # <------ No numbers Sweet VerySweet } At line:4 char:8 + Dry + ~ Missing closing '}' in statement block or type definition. At line:8 char:1 + } + ~ Unexpected token '}' in expression or statement. + CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException + FullyQualifiedErrorId : MissingEndCurlyBrace
Enums have integer values
Every enumerator value has a corresponding integer value. When you create an enumerator in Windows PowerShell (by using the Add-Type cmdlet or the enum keyword), the parser assigns a default integer value to each enum value. These default integer values begin with zero and increase by 1. The values are assigned in the order they are listed in the enumerator.
For example, when I created my WineSweetness enum, Windows PowerShell assigned the values 0 – 4 to the enum values.
enum WineSweetness { VeryDry Dry Moderate Sweet VerySweet } |
To find the values in any enumerator, call the GetValues static method of the System.Enum type:
PS C:\> [System.Enum]::GetValues([WineSweetness]) VeryDry Dry Moderate Sweet VerySweet
To find the underlying integer value of each enum value, cast the enum value to an integer (thanks to Keith Babinec for this trick).
PS C:\> [System.Enum]::GetValues([WineSweetness]) | foreach { [int]$_ } 0 1 2 3 4
Let’s put them together. This command creates a custom object (I love PSCustomObject!) with the names and integer value of each enumerator value.
PS C:\> [System.Enum]::GetValues([WineSweetness]) | foreach { [PSCustomObject]@{ValueName = $_; IntValue = [int]$_ } } ValueName IntValue --------- -------- VeryDry 0 Dry 1 Moderate 2 Sweet 3 VerySweet 4
You can also assign integer values, and expressions that return an integer value, to your enum values. The values that you assign override the default zero-based values. All enum values are constants, so you can’t change them after you assign them.
PS C:\> enum WineSweetness { Sweet = 4 VeryDry = 2 * 4 + 2 Moderate = 6 VerySweet = 2 Dry = 8 } [System.Enum]::GetValues([WineSweetness]) | foreach {[PSCustomObject]@{ValueName = $_; IntValue = [int]$_} } ValueName IntValue --------- -------- VerySweet 2 Sweet 4 Moderate 6 Dry 8 VeryDry 10
Using an enumerator
When you use an enumerator, you can specify either a value name or its integer value.
enum WineSweetness { VeryDry = 1 Dry = 2 Moderate = 3 Sweet = 4 VerySweet = 5 } |
I’ll create an instance of my Wine class and set its Sweetness property, which has a type of WineSweetness, to VeryDry (my personal favorite).
PS C:\>$PSWine = [Wine]::new("PSCabernet") PS C:\>$PSWine.Sweetness = VeryDry
I can use the dot method to get the string representation of the Sweetness property value:
PS C:\> $PSWine.Sweetness VeryDry
Or, use the Value__ (that’s 2 underscores) property of all enum values to get its integer value.
PS C:\> $PSWine.Sweetness.value__ 1
If I try to change the value of the Sweetness property to a value that’s not in the enumerator, I get that awesome error that lists the valid values.
PS C:\ps-test> $PSWine.Sweetness = "TooSweet" Exception setting "Sweetness": "Cannot convert value "TooSweet" to type "WineSweetness". Error: "Unable to match the identifier name TooSweet to a valid enumerator name. Specify one of the following enumerator names and try again: VeryDry, Dry, Moderate, Sweet, VerySweet"" At line:1 char:1 + $PSWine.Sweetness = "TooSweet" + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], SetValueInvocationException + FullyQualifiedErrorId : ExceptionWhenSetting
Just so I use a valid value, I can assign either a string or a integer value.
PS C:\> $PSWine.Sweetness = Dry PS C:\> $PSWine.Sweetness Dry PS C:\> $PSWine.Sweetness = 3 PS C:\> $PSWine.Sweetness Moderate
When you sort objects by an enumerated property value, the Sort-Object cmdlet sorts by integer value. For example, I have some wines in a $myWines variable.
PS C:\ps-test> $myWines Name : Great Duck Winery : Escalante Winery Year : 2003 isSparkling : True Color : White Sweetness : VerySweet Description : Rich and magnificent Rating : {98, 97} Name : PSCabernet Winery : Chateau Snover Year : 2006 isSparkling : False Color : Red Sweetness : VeryDry Description : Pithy Rating : {96} Name : Shiraz Winery : SAPIEN Vineyards Year : 1990 isSparkling : False Color : Red Sweetness : Dry Description : Bold and innovative Rating : {100}
When I sort them by the value of the Sweetness property, I get them in ascending integer value order. I used a calculated property to add the integer value to the table.
PS C:\ps-test> $mywines | Sort-Object -Property Sweetness | Format-Table -Property Name, ` Sweetness, ` @{Label="IntSweet"; Expression={$_.Sweetness.Value__}} -AutoSize Name Sweetness IntSweet ---- --------- -------- PSWine VeryDry 1 Shiraz Dry 2 Great Duck VerySweet 5
The introduction of the enum keyword has really made enumerations available to everyone. Yet another tool for your Windows PowerShell toolbox.
June Blender is a technology evangelist at SAPIEN Technologies, Inc. You can reach her at juneb@sapien.com or follow her on Twitter at @juneb_get_help.
![[Google+]](http://www.sapien.com/blog/wp-content/uploads/GooglePlus.gif)