Reputation: 573
the problem is quite simple. I have a function with two arguments and I would like to be able to call it without providing the first one when it has its default value, without naming the second one.
for example (this is not my real function but a simple example to show what I mean)
function foo
{ param([int]$int=-1,[string]$str="''")
$int
$str
}
I was hoping that forcing the type in the argument list will make PS bind the right value to the right argument but it seams that it's not the case
PS C:\> foo
-1
''
PS C:\> foo 1
1
''
PS C:\> foo 1 x
1
x
PS C:\> foo -str x
-1
x
PS C:\> foo x
foo : Impossible de traiter la transformation d'argument sur le paramètre «int». Impossible de convertir la valeur «x» en type «System.Int32». Erreur: «Le format de la chaîne
d'entrée est incorrect.»
Au caractère Ligne:1 : 5
+ foo x
+ ~
+ CategoryInfo : InvalidData : (:) [foo], ParameterBindingArgumentTransformationException
+ FullyQualifiedErrorId : ParameterArgumentTransformationError,foo
is what I want possible in PS ?
here's a better example of what I'm trying to do
[cmdletbinding()]
param(
[parameter()]
[string[]]$___where=@('*')
)
function InvokeOn {
[cmdletbinding()]
param(
[string[]]
${___names}=@('*'),
[scriptblock]
${___block}
)
$___doExec = ($___names -contains '*')
foreach($___name in $___names) { $___doExec = ($___doExec -or ($___where -contains $___name)) }
if($___doExec) { &$___block }
}
$null = new-item alias:__LOG__ -value InvokeOn
__LOG__ c1 { Write-host '-- 1 --' }
__LOG__ c2 { Write-host '-- 2 --' }
__LOG__ c1,c2 { Write-host '-- 1 or 2 --' }
__LOG__ { Write-host 'always, defaulted' }
__LOG__ -___block { Write-host 'always, named' }
and a few run
PS C:\> .\test
always, named
PS C:\> .\test c1
-- 1 --
-- 1 or 2 --
always, named
PS C:\> .\test c2
-- 2 --
-- 1 or 2 --
always, named
PS C:\> .\test c2,c1
-- 1 --
-- 2 --
-- 1 or 2 --
always, named
as you can see, __LOG__ { Write-host 'always, defaulted' }
never fires as PS binds the scriptblock the wrong parameter.
Parameters names are on purpose complex and should not even be known by the developer using the aliased function.
Swapping the parameters is not practical as the scriptblock may be long and even for short ones, make the case where __LOG__
will fire less readable.
applying majkinetor idea, I modified my code this way
function InvokeOn {
[cmdletbinding()]
param(
[string[]]
${___names} = @('*'),
[scriptblock]
${___block}
)
if(!$PSBoundParameters.ContainsKey('___block')) { $___names,$___block = @('*'),[scriptblock]::create($___names[0]) }
$___doExec = ($___names -contains '*')
foreach($___name in $___names) { $___doExec = ($___doExec -or ($___where -contains $___name)) }
if($___doExec) { &$___block }
}
and now it works as expected :)
Upvotes: 1
Views: 409
Reputation: 200203
I consider hiding parameter names from the user and evaluating parameters solely based on their type a Bad Idea™ and strongly recommend against doing this.
With that said, if you for some obscure reason you absolutely must have it this way, I'd drop named parameters entirely and use the $args
automatic variable instead.
function foo {
# define default values for your "parameters"
$int = -1
$str = "''"
...
# evauluate $args and override default values
foreach ($arg in $args) {
switch ($arg.GetType().FullName) {
'System.Int32' { $int = $arg }
'System.String' { $str = $arg }
...
default { throw "Unrecognized type ${_}." }
}
}
# handle missing arguments if required
# Example:
# if ($args.Count -eq 0) { throw "No arguments provided." }
...
}
Upvotes: 0
Reputation: 9036
You could do something like:
function foo
{
param ($int=-1,[string]$str="''")
if ($int.gettype().Name -eq 'String') { $str = $int; $int = -1 }
$int
$str
}
Notice - $int
must not have type.
Upvotes: 3