PowerShell: Why does [Parameter(Mandatory = $True)] on one parameter affect type casting behavior of another parameter?

Viewed 405

Does someone know why Mandatory attribute on parameter $a affects the type casting behaviour of parameter $b? In the example the array should be cast to a string.

function test ([Parameter(Mandatory = $True)] [string] $a, [string] $b) {
  $a; $b
}
$b = "a", "b", "c"
test -a "my string" -b $b

When this code chunk is executed it produces error: test : Cannot process argument transformation on parameter 'b'. Cannot convert value to type System.String. At line:1 char:31

If I remove the Mandatory attribute from $a it works fine:

function test ([string] $a, [string] $b) {
  $a; $b
}
$b = "a", "b", "c"
test -a "my string" -b $b

Thanks in advance for feedback

2 Answers

By adding the [Parameter()] attribute, you are implying the [CmdletBinding()] attribute (i.e. turning the function into an Advanced Function). If you look at the source code for the behaviour when using [CmdletBinding()], you'll see that it explicitly disallows conversion of an array to a string, which is what you're trying to do.

To see that it is specific to arrays, try, for example, $b = Get-Date (i.e. pass a DateTime object). The conversion to string works fine.

To complement boxdog's helpful answer:

Indeed, the difference in behavior comes down to whether your function is a simple one (no [CmdletBinding()] attribute and/or [Parameter()] attributes) or an advanced - cmdlet-like - one.

Despite PowerShell's generally very helpful automatic type conversions, you could argue that even simple functions should never have allowed implicitly converting an array to a scalar [string] parameter value, because:

  • The way that arrays are stringified (converted to a single string) is somewhat arbitrary: by default, the (stringified) elements of the array are space-separated (a script block ({...}) without the aforementioned parameters is effectively a simple function).

    • & { param([string] $s) "[$s]" } 'one', 'two' # -> '[one two]'
  • What's more, this behavior depends on whether the $OFS preference variable was set to an arbitrary separator string.

    • $OFS='!'; & { param([string] $s) "[$s]" } 'one', 'two' # -> '[one!two]'

In short:

  • It would be reasonable to always require deliberate conversion of an array meant to be passed to a parameter that expects a scalar.

  • However, given PowerShell's commitment to backward compatibility, the existing behavior of simple functions is unlikely to change.

Related