Primoz
Primoz

Reputation: 4331

Function overloading in PowerShell

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:

Upvotes: 39

Views: 37995

Answers (5)

Safwan
Safwan

Reputation: 360

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

Upvotes: 0

Jonathan Asbury
Jonathan Asbury

Reputation: 45

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

Upvotes: 1

ZEE
ZEE

Reputation: 3193

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

Upvotes: 4

OldFart
OldFart

Reputation: 2479

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

Upvotes: 7

Roman Kuzmin
Roman Kuzmin

Reputation: 42063

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

Upvotes: 40

Related Questions