In C#, function overloading has historically appeared something like the following, where each overload adds some amount of parameters on top of the simpler signatures:
public void Initialize(int version);
public void Initialize(int version, string workspaceName);
public void Initialize(int version, string workspaceName, Path workspaceRoot, bool force);
In Powershell, overloads such as these are not possible; however, some approximation of function overloading is offered through System.Management.Automation.ParameterAttribute’s ParameterSetName property. This allows us to declare parameters as members of certain parameter sets, which allows us to effectively define separate signatures for our functions. The simplistic case found in most ParameterSetName examples is something like this:
function test-param
{
[CmdletBinding(DefaultParametersetName="p2")]
param(
[Parameter(ParameterSetName="p1",Position=0)]
[String]
$d,
[Parameter(ParameterSetName="p2", Position=0)]
[String]$i
)
switch ($PsCmdlet.ParameterSetName)
{
"p1" { Write-Host ([DateTime]$d); break}
"p2" { Write-Host ([INT]$i); break}
}
}
Get-help on this function yields the following:
PS D:\.ws> get-help test-param
test-param [[-i] <String>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
test-param [[-d] <String>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
This basic type of example isn’t incredibly for complex usages of parameter sets, however, because the parameter sets are entirely mutually exclusive. But what if we want some parameters to be members of all parameter sets? More experienced users might suggest that this is a case for the special “AllParameterSets” parameter set, which according to MSDN is the default if a parameter set is not specified. However, consider the following:
PS D:\.ws> function test-param
{
[CmdletBinding(DefaultParametersetName="p2")]
param
(
[Parameter(ParameterSetName="p1",Position=0)]
[String]
$d,
[Parameter(ParameterSetName="p2", Position=0)]
[String]$i,
[Parameter(ParameterSetName="AllParameterSets")]
[String]$x
)
}
PS D:\.ws> get-help test-param
test-param [[-i] <String>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
test-param [[-d] <String>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
test-param [-x <String>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Apparently when “AllParameterSets” is specified as the parameter set, it is in fact not included in all parameter sets as one might expect. This can be seen by omitting the declaration:
PS D:\.ws> function test-param
{
[CmdletBinding(DefaultParametersetName="p2")]
param
(
[Parameter(ParameterSetName="p1",Position=0)]
[String]
$d,
[Parameter(ParameterSetName="p2", Position=0)]
[String]$i,
[String]$x
)
}
PS D:\.ws> get-help test-param
test-param [[-i] <String>] [-x <String>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
test-param [[-d] <String>] [-x <String>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Now, the $x parameter appears in all function signatures like we’d expect.
Finally, the most complex case. What if we want one parameter to appear in some, but not all signatures? The workspaceName parameter given in the original example fits this description. Perhaps a key to this solution is realizing that multiple Parameter properties can be declared for each parameter in the param block. This allows us to establish a parameter set for each signature, and decorate each parameter with a Parameter attribute for each set it belongs to. Consider the following:
PS D:\.ws> function Initialize-Something
{
[CmdletBinding()]
param
(
[Parameter(ParameterSetName="version")]
[Parameter(ParameterSetName="workspaceName")]
[Parameter(ParameterSetName="createWorkspace")]
[int] $Version,
[Parameter(ParameterSetName="workspaceName")]
[Parameter(ParameterSetName="createWorkspace")]
[string] $WorkspaceName,
[Parameter(ParameterSetName="createWorkspace")]
[string] $WorkspaceRoot,
[Parameter(ParameterSetName="createWorkspace")]
[switch] $Force
)
}
PS D:\.ws> get-help initialize-something
Initialize-Something [-Version <Int32>] [-WorkspaceName <String>] [-WorkspaceRoot <String>] [-Force] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Initialize-Something [-Version <Int32>] [-WorkspaceName <String>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Initialize-Something [-Version <Int32>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
PS D:\.ws> initialize-something -workspacename "test"
Initialize-Something : Parameter set cannot be resolved using the specified named parameters.
At line:1 char:21
+ initialize-something <<<< -workspacename "test"
+ CategoryInfo : InvalidArgument: (:) [Initialize-Something], ParameterBindingException
+ FullyQualifiedErrorId : AmbiguousParameterSet,Initialize-Something
Powershell claims it can’t determine a parameter set, more than likely because WorkspaceName appears in multiple signatures. For the record, this also occurs when specifying only the -Version, or for that matter any signature other than the most complex. This can be alleviated somewhat with the [CmdletBinding(DefaultParameterSetName=”version”)] or something, but it’s not a proper solution.
So after all this, my question: How can I achieve the kind of signatures I’m looking for? Do I need to create a set of overly-explicit switches such as -VersionMode, -WorkspaceNameMode, -CreateWorkspaceMode to specify which mode I want it to run, essentially negating the niceness of parameter set detection? Perhaps a mode enum? Can this be done through some elegance using ParameterAttribute’s Mandatory and Position properties?
Thanks!
If you are trying to emulate C# function overloads the easiest way is to simply allow for “optional” parameters e.g.:
The parameters not specified by the invoker will default to $null (the switch will default to $false). You can also provide default values for the parameters like so: