39

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
1
  • 1
    "Parameter Sets" are basically equivalent to OO method overloading. They are a bit trickier at times due to poewrshell's type-coercion heavy parser. Commented Oct 17, 2010 at 10:12

5 Answers 5

40

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
Sign up to request clarification or add additional context in comments.

2 Comments

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)
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[]]).
7

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"
        }
    }
}

Comments

4

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...

Comments

1

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

Comments

0

There are some good answers already posted but I thought I would share another way of doing this; while technically this method is not Function Overloading, it does provide a similar functionality which might be sufficient, depending on your requirements.

The idea is to use a function with only 1 parameter of type [HashTable]. All the parameters that you need to pass to your function will be passed through this single hash table parameter. The code below should be easy enough to see how it works.

Function OverLoadedFunction
{
 Param
 (
  [HashTable] $Parameters
 )

 # Default parameter values if desired.
 [String] $Server = 'Default Server'
 [Int]    $Port   = 22

 ForEach ($Parameter In $Parameters.Keys)
 {
  Switch ($Parameter)
  {
   'Server' {$Server = $Parameters.$Parameter.Trim(); Break} 
   'Port'   {$Port   = $Parameters.$Parameter       ; Break}
   Default  {Write-Host 'Not a valid parameter'}
  }
 }
  Write-Host $Server on port $Port
}

Cls

OverLoadedFunction
OverLoadedFunction @{server='new server'}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.