You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The proposal below is undoubtedly a change that cannot be made without breaking existing code.
However, I don't think the existing problems can be fixed while maintaining backward compatibility.
Therefore, use of an implementation of this proposal would have to be on an opt-in basis by future function / script authors, via an optional feature or, should there ever be one, a "vNext" PowerShell version that is allowed to break backward compatibility in the interest of fixing many longstanding problems.
This will probably have to become an RFC, if there's sufficient interest - this issue is meant to get the conversion started and gauge that interest.
Summary of the new feature/enhancement
There are several problems with the existing parameter-set handling:
$PSCmdlet.ParameterSetName, meant to reflect the parameter set in effect (selected by the combination of arguments and/or pipeline input), obscurely and confusingly reflects __AllParameterSets in argument-less invocations or in invocations that comprise parameters without explicit set association.
__AllParameterSets is also the internal name of the "meta" set automatically assigned to parameters without explicit set association, and it signals that a given parameter belongs to all parameter sets (if any exist beyond the implied unnamed one).
As such, the name __AllParameterSets should be considered an implementation detail, and making it do double duty as the name of the effective set is obscure and confusing.
Ill-defined parameter sets do not become apparent until runtime, as opposed to parse time, including misspelled attribute property names. Problems may not be discovered until later, when a specific combination of arguments and inputs is used on invocation (e.g., unintentionally allowing / disallowing argument-less invocation).
Designating an otherwise undefined default parameter set as the default - e.g., [CmdletBinding(DefaultParameterSetName='PossiblyAccidentalDefault')] is quietly accepted (and an invocation without arguments or only with parameters not explicitly associated with sets make it the effective on).
While this technique is actually currently required to implement a function that explicitly allows passing no arguments alongside parameters with explicit set associations (it is also the easiest way to allow invocations binding all-parameter-sets parameters only), it is non-obvious and brittle.
On a minor note, the Name suffix in the attribute property names seems unnecessarily verbose (ParameterSetName, DefaultParameterSetName).
Enforce the following at parse time to signal that there's a fundamental problem with the function / script that needs resolving, as opposed to the current situational failures that depend on the specifics of a given invocation. This also has the advantage of being able to provide specific error messages with clear resolution directions.
If an explicit parameter set is associated with at least one parameter, enforce having to explicitly designate a default parameter set in [CmdletBinding()]. This avoids the obscurity of the current, situational inference of the default and instead signals explicit intent.
Do not permit designating an undefined parameter set as the default in the [CmdletBinding()] attribute - the name must refer to a set named in at least one [Parameter()] attribute.
If feasible, ensure up front that there are no unknown / mistyped property names in the [CmdletBinding()], [Parameter()], ... attributes.
Make '' (empty string) the (non)-name for the implied, unnamed parameter set to become effective in argument-less invocations and invocations with all-parameter-sets parameters only.
[CmdletBinding(DefaultParameterSetName='')] will signal the explicit intent to allow invocations without arguments altogether, as well as invocations comprising all-parameter-sets parameters only.
$PSCmdlet.DefaultParameterSetName will then reflect '' (the empty string) in such invocations.
Allow omitting the wordy Name suffix from the DefaultParameterSetName and ParameterSetName attribute properties.
Example
Consider a function Write-Message, which should wrap Write-Host as follows:
Print a default message, if no arguments are passed at all.
If only a -Message argument is passed, print that message as-is.
If one of the mutually exclusive -AsError or -AsWarning switches is passed, print the (default or explicit) message in a switch-specific color.
Current declaration:
functionWrite-Message {
# * Without artificially designating a default parameter set, # argument-less and -Message-only invocations don't work.# * If 'ArtificalName' happens to be a leftover / mistyped name, you may # accidentally be enabling undesired invocations.
[CmdletBinding(DefaultParameterSetName='ArtificalName')]
param(
# Optional message that has a default.
[string] $Message='Completed.',# Note: Whether you use `Mandatory` or not makes no difference here.
[Parameter(ParameterSetName='err')] [switch] $AsError,
[Parameter(ParameterSetName='warn')] [switch] $AsWarning
)
$writeHostArgs=@{ Object=$Message }
if ($AsError) { $writeHostArgs.ForegroundColor='Red' }
elseif ($AsWarning) { $writeHostArgs.ForegroundColor='Yellow' }
Write-Host@writeHostArgs
}
Declaration with the proposal implemented:
functionWrite-Message {
# * Designating '' as the default parameter set unambiguously signals the intent to # allow argument-less and all-parameter-sets-parameters-only invocations.# * Any other name would have to refer to a set explicitly mentioned in at least one# [Parameter()] attribute.
[CmdletBinding(DefaultParameterSet ='')]
param(
# Optional message that has a default.
[string] $Message='Completed.',# Note: Whether you use `Mandatory` or not makes no difference here.
[Parameter(ParameterSet ='err')] [switch] $AsError,
[Parameter(ParameterSet ='warn')] [switch] $AsWarning
)
$writeHostArgs=@{ Object=$Message }
if ($AsError) { $writeHostArgs.ForegroundColor='Red' }
elseif ($AsWarning) { $writeHostArgs.ForegroundColor='Yellow' }
Write-Host@writeHostArgs
}
Important:
The proposal below is undoubtedly a change that cannot be made without breaking existing code.
However, I don't think the existing problems can be fixed while maintaining backward compatibility.
Therefore, use of an implementation of this proposal would have to be on an opt-in basis by future function / script authors, via an optional feature or, should there ever be one, a "vNext" PowerShell version that is allowed to break backward compatibility in the interest of fixing many longstanding problems.
This will probably have to become an RFC, if there's sufficient interest - this issue is meant to get the conversion started and gauge that interest.
Summary of the new feature/enhancement
There are several problems with the existing parameter-set handling:
In the absence of explicitly designating a default parameter set, PowerShell tries to infer one, but does so inconsistently (Advanced functions require specifying a default parameter set with two or more explicit parameter sets #11143) and sometimes incorrectly (Binding a parameter via the pipeline can break implicit parameter-set selection #11235).
$PSCmdlet.ParameterSetName, meant to reflect the parameter set in effect (selected by the combination of arguments and/or pipeline input), obscurely and confusingly reflects__AllParameterSetsin argument-less invocations or in invocations that comprise parameters without explicit set association.__AllParameterSetsis also the internal name of the "meta" set automatically assigned to parameters without explicit set association, and it signals that a given parameter belongs to all parameter sets (if any exist beyond the implied unnamed one).__AllParameterSetsshould be considered an implementation detail, and making it do double duty as the name of the effective set is obscure and confusing.Ill-defined parameter sets do not become apparent until runtime, as opposed to parse time, including misspelled attribute property names. Problems may not be discovered until later, when a specific combination of arguments and inputs is used on invocation (e.g., unintentionally allowing / disallowing argument-less invocation).
Designating an otherwise undefined default parameter set as the default - e.g.,
[CmdletBinding(DefaultParameterSetName='PossiblyAccidentalDefault')]is quietly accepted (and an invocation without arguments or only with parameters not explicitly associated with sets make it the effective on).On a minor note, the
Namesuffix in the attribute property names seems unnecessarily verbose (ParameterSetName,DefaultParameterSetName).Proposed technical implementation details (optional)
Enforce the following at parse time to signal that there's a fundamental problem with the function / script that needs resolving, as opposed to the current situational failures that depend on the specifics of a given invocation. This also has the advantage of being able to provide specific error messages with clear resolution directions.
If an explicit parameter set is associated with at least one parameter, enforce having to explicitly designate a default parameter set in
[CmdletBinding()]. This avoids the obscurity of the current, situational inference of the default and instead signals explicit intent.Do not permit designating an undefined parameter set as the default in the
[CmdletBinding()]attribute - the name must refer to a set named in at least one[Parameter()]attribute.If feasible, ensure up front that there are no unknown / mistyped property names in the
[CmdletBinding()],[Parameter()], ... attributes.Make
''(empty string) the (non)-name for the implied, unnamed parameter set to become effective in argument-less invocations and invocations with all-parameter-sets parameters only.[CmdletBinding(DefaultParameterSetName='')]will signal the explicit intent to allow invocations without arguments altogether, as well as invocations comprising all-parameter-sets parameters only.$PSCmdlet.DefaultParameterSetNamewill then reflect''(the empty string) in such invocations.Allow omitting the wordy
Namesuffix from theDefaultParameterSetNameandParameterSetNameattribute properties.Example
Consider a function
Write-Message, which should wrapWrite-Hostas follows:-Messageargument is passed, print that message as-is.If one of the mutually exclusive
-AsErroror-AsWarningswitches is passed, print the (default or explicit) message in a switch-specific color.Current declaration:
Declaration with the proposal implemented: