Search code examples
powershellparametersenumsarguments

PowerShell: How to accept an array of custom enum types as a function param?


The example below is simply for illustration. I'm not looking for optimization or any sort of workaround solutions as this function obviosuly doesn't do anything useful - again, it's simply for presenting:

  • Accepting an entire enum type/structure as an argument
  • Doing something (anything) with the passed enum argument/param.
    • In this case it simply retrieves the enum name that corresponds to the int value of 2.

The following works to accept a single enum type as an argument and outputs the expected value:

function EnumTest
{
    param ([System.Object]$EnumObject)
    
    BEGIN {}
    PROCESS
    {
        [System.Enum]::GetName($EnumObject, 2)
    }
    END {}
}

enum MyCustomEnum
{
    Balloon     = 1
    Bicycle     = 2
    Cloud       = 3
}

enum MyOtherCustomEnum
{
    Crayon      = 1
    Dog         = 2
    Elephant    = 3
}

EnumTest -EnumObject MyCustomEnum

Output:

Bicycle



Questions:

  1. How can I get the param $EnumObject to accept an array of enum types (the entire enum structure, not just one of its names/values), e.g.:

    EnumTest -EnumObject MyCustomEnum, MyOtherCustomEnum

    • [System.Object[]]$EnumObject doesn't work
    • [System.Enum[]]$EnumObject doesn't work
      • Why doesn't this work?
    • [array]$EnumObject doesn't work
  2. What is the appropriate way to pass an entire enum type as an argument?

    • EnumTest -EnumObject MyCustomEnum appears to pass the enum structure's name "MyCustomEnum" as a string instead of the intended (entire) enum object [MyCustomEnum].

Solution

  • PowerShell interprets the bare word token MyCustomEnum in this statement:

    EnumTest -EnumObject MyCustomEnum
    

    ... as a string literal, no different than if you'd done:

    EnumTest -EnumObject "MyCustomEnum"
    

    If you just want to pass the type as a parameter argument, place a type literal in a subexpression like this:

    EnumTest -EnumObject $([MyCustomEnum])
    

    [object]/[System.Object] as parameter type will work here, since every type ultimately inherits from [object] anyway.

    If you want to restrict parameter arguments to only types, use [type] as the parameter type:

    function EnumTest
    {
        param (
             [type]$EnumType
        )
        
        process {
            if($EnumType.IsEnum){
                [System.Enum]::GetName($EnumObject, 2)
            }
        }
    }
    

    If you want to pass an arbitrary number of enums either as positional arguments or via the pipeline, add an appropriate [Parameter()] attribute and (for trailing argument support) change the parameter type to an array of [type]:

    function EnumTest
    {
        param (
            [Parameter(ValueFromPipeline, ValueFromRemainingArguments)]
            [type[]]$EnumType
        )
        
        process {
            foreach($type in $EnumType){
                if($type.IsEnum){
                    [System.Enum]::GetName($type, 2)
                }
            }
        }
    }
    

    Now all of the below invocation scenarios will work:

    EnumTest $([MyCustomEnum]) $([MyOtherCustomEnum]) 
    # or 
    EnumTest @([MyCustomEnum], [MyOtherCustomEnum]) 
    # or
    [MyCustomEnum],[MyOtherCustomEnum] |EnumTest 
    # or 
    $enumTypes = [MyCustomEnum],[MyOtherCustomEnum]
    EnumTest -EnumType $enumTypes
    # or, if you wanna get funky: 
    $paramArgs = @{ EnumType = 'MyCustomEnum,MyOtherCustomEnum' -split ',' -as [type[]] }
    EnumTest @paramArgs
    

    A previous version of the question made it sound like the user wanted to store multiple enum values as a scalar value, hence this answer:

    It looks like you'll want to decorate the enum type with the Flags attribute - this will allow you to store compound values:

    [Flags()]
    enum MyCustomEnum
    {
        FirstName   = 1
        SecondName  = 2
        ThirdName   = 4
    }
    

    Now multiple flags can be stored in a single instance of MyCustomEnum:

    PS ~> $value = [MyCustomEnum]'FirstName, ThirdName'
    PS ~> $value.value__ # the instance now stores the value of `FirstName -bor ThirdName`
    5
    

    The presence or absence of each individually defined value can be interrogated with the Enum.HasFlag method:

    PS ~> $value = [MyCustomEnum]'FirstName, ThirdName'
    PS ~> $value.HasFlag('FirstName')
    True
    PS ~> $value.HasFlag('SecondName')
    False