Quantcast
Channel: Windows PowerShell - SAPIEN Blog
Viewing all articles
Browse latest Browse all 308

Enumerators in Windows PowerShell 5.0

$
0
0

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.

image

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+] [Twitter] [Facebook] [LinkedIn] [StumbleUpon] [Digg] [Reddit] [Google Bookmark]

Viewing all articles
Browse latest Browse all 308

Trending Articles