Function overloading in PowerShell

30,752

Solution 1

In PowerShell functions are not overloaded. The last definition overrides the previous in the same scope or hides the previous in a parent scope. Thus, you should create a single function and provide a way to distinguish its call mode by arguments.

In V2 you may use an advanced function, see help about_Functions_Advanced_Parameters and avoid some manual coding on resolving parameter set ambiguities:

# advanced function with 3 parameter sets
function Backup-UsersData
(
    [Parameter(Position=0, ParameterSetName="user")]
    [string]$user,
    [Parameter(Position=0, ParameterSetName="array")]
    [object[]]$array,
    [Parameter(Position=0, ParameterSetName="all")]
    [switch]$all
)
{
    # use this to get the parameter set name
    $PSCmdlet.ParameterSetName
}

# test
Backup-UsersData -user 'John'
Backup-UsersData 1, 2
Backup-UsersData -all

# OUTPUT:
# user
# array
# all

Note that this mechanism is sometimes strange. For example in the first test we have to specify parameter name -user explicitly. Otherwise:

Backup-UsersData : Parameter set cannot be resolved using the specified named parameters.
At C:\TEMP\_101015_110059\try2.ps1:21 char:17
+ Backup-UsersData <<<<  'John'
    + CategoryInfo          : InvalidArgument: (:) [Backup-UsersData], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : AmbiguousParameterSet,Backup-UsersData

In many cases standard, not advanced, function with mixed parameters will do:

function Backup-UsersData
(
    [string]$user,
    [object[]]$array,
    [switch]$all
)
{
    if ($user) {'user'}
    elseif ($array) {'array'}
    elseif ($all) {'all'}
    else {'may be'}
}

Backup-UsersData -user 'John'
Backup-UsersData -array 1, 2
Backup-UsersData -all
Backup-UsersData

But in this case you should resolve (or accept and ignore) ambiguities, e.g. to decide what to do if, say:

Backup-UsersData -user 'John' -array 1, 2 -all

Solution 2

Here is a variant of Roman's answer that I think is a little more flexible:

function Backup
{
    [CmdletBinding(DefaultParameterSetName='Users')]
    Param (
        [parameter(mandatory=$true, ParameterSetName='Users', position=0, ValueFromPipeline=$true)][string[]]$User,
        [parameter(mandatory=$true, ParameterSetName='AllUsers')][switch]$All
    )

    Begin
    {
        if ($All) { $User = @('User1', 'User2', 'User3') }
    }

    Process
    {
        foreach ($u in $User)
        {
            echo "Backup $u"
        }
    }
}

Solution 3

1) Build a class...

class c1 { 
    [int]f1( [string]$x ){ return 1 } 
    [int]f1( [int ]$x ){ return 2 }
    }

1+) Use STATIC METHODS if you prefer to call them without instantiation...

class c1 { 
    static [int] f1( [string]$x ){ return 1 } 
    static [int] f1( [int]$x ){ return 2 } 
    }

2) Call the methods in class or object... overload works OK

$o1 = [c1]::new()
o1.f1( "abc" ) ~> returns 1
o1.f1( 123 )   ~> returns 2

-OR-


[c1]::f1( "abc" ) ~> returns 1
[c1]::f1( 123 )   ~> returns 2

3) If (like me)
you want to have "Overloaded Functions" placed in a libraries...
so your users can use them transparently...
from code or from Interactive Command Line (REPL)...

the closest I could came to
"Overloading functions in Powershell"
was something like this:

function Alert-String() { [c1]::f1( "abc" ) }
function Alert-Strings(){ [c1]::f1( 123 ) }
function Alert-Stringn(){ [c1]::f1( 123 ) }

Maybe in PS-Core v8??? ;-)

Hope it helps...

Solution 4

If you use PSObject instead of Object to define your parameter type, it should work. For example, The function Get-Control, know's how to overload based on type string or template and can be called using the positional value:

    Get-Control "A-Name-Of-A-Control"
    Get-Control $template

To make the overload work, use PSObject as follows:

Function Get-Control {
    Param(
        [Parameter(Mandatory=$False,ParameterSetName="ByTemplate",Position=0)]
        [PSObject]
        $Template,

        [Parameter(Mandatory=$False,ParameterSetName="ByName",Position=0)]        
        [String]
        $Name,

        [Parameter(Mandatory=$False)] 
        [Switch]
        $List
      ) 
   ... # remaining code removed for brevity
Share:
30,752
Primoz
Author by

Primoz

Updated on October 09, 2020

Comments

  • Primoz
    Primoz over 3 years

    Can you overload functions in PowerShell?

    I want to my function to accept a string, array or some switch.

    An example of what I want:

    • Backup-UsersData singleUser
    • Backup-UsersData @('Alice', 'Bob', 'Joe')
    • Backup-UsersData -all
  • Dave F
    Dave F about 3 years
    It should be mentioned that class functions can be overloaded. This is done similarly to how it is done in other languages, by declaring multiple functions with the same names but different types of parameters. For example, a class with the functions [string] val([int] $i) { return "Int: $i" } and [string] val([string] $s) { return "String: $s" } will output Int: 1 for $t.val(1) and String: abc for $t.val('abc') (NOTE: I verified that this code worked using PowerShell 5.1 and 7.1)
  • anto418
    anto418 over 2 years
    Also it should be noted that Powershell (on 5.1, possibly earlier) can and will coerce single values into [Object[]] parameters: the function will get an array with the original object as its single value. This works with any type as long as the single value can be coerced into the type contained by the array (ie "plop" can be coerced into [String[]] but not into [Int32[]]).