geoced
geoced

Reputation: 722

Powershell arrays, functions and reference

I understand Powershell arrays are immutable objects and like all objects, are passed by reference to functions. But what I don't understand in the below example is why $b doesn't have the same values as $a.

The only difference is that $b is not strongly typed. Does that mean that Test-ByRefArray casts $b from [object[]] to [string[]], a new object is actually created, which would explain why changes made by Test-ByRefArray on this new object have no impact on the original object ?

function Test-ByRefArray {
    param (
        [string[]] $Array1,
        [string[]] $Array2
    )
    $Array1[0] = "Modified by Test-ByRefArray"
    $Array2[0] = "Modified by Test-ByRefArray"
}

function Test-Array {
    [string[]] $a = 'hello', 'world'
    $b = 'hello', 'world'
    $c = $a #by ref: if a is updated, so is $c

    Test-ByRefArray -Array1 $a -Array2 $b

    $a -join ", "
    $b -join ", "
    $c -join ", "
}

Test-Array

Output:

Modified by Test-ByRefArray, world
hello, world
Modified by Test-ByRefArray, world

Upvotes: 2

Views: 193

Answers (1)

Mathias R. Jessen
Mathias R. Jessen

Reputation: 174435

What happens here is that the type of $a satisfies the type constraint of $Array1 - it's already a [string[]], and PowerShell passes it to your function as-is - so $Array1 now holds a reference to the exact same array as $a has a reference to at the call site.

$b, on the other hand, is implicitly typed [object[]] - and [object[]] does not satisfy the type constraint [string[]] on $Array2:

PS ~> [string[]].IsAssignableFrom([object[]])
False

Since PowerShell wants to be helpful, it converts the array to a new [string[]], and so $Array2 refers to this new array that has been created during parameter binding.

For this reason, the contents of the array referenced by $b is never modified - $Array2 references a completely different array.


We can observe whether type conversion (or type coercion) occurs in the output from Trace-Command, here in Windows PowerShell 5.1:

PS ~> Trace-Command -Expression {&{param([string[]]$ParamArray)} @([string[]]@())} -Name ParameterBinding -PSHost
DEBUG: ParameterBinding Information: 0 : BIND arg [System.String[]] to parameter [ParamArray]
DEBUG: ParameterBinding Information: 0 :     Executing DATA GENERATION metadata: [System.Management.Automation.ArgumentTypeConverterAttribute]
DEBUG: ParameterBinding Information: 0 :         result returned from DATA GENERATION: System.String[]
DEBUG: ParameterBinding Information: 0 :     BIND arg [System.String[]] to param [ParamArray] SUCCESSFUL

The output might look a bit confusing at first, but here we can see PowerShell using the type constraint of the $ParamArray parameter and determines that the input argument is already of type [string[]] - no actual work needed.

Now let's pass an implicitly typed array instead:

PS ~> Trace-Command -Expression {&{param([string[]]$ParamArray)} @(@())} -Name ParameterBinding -PSHost
DEBUG: ParameterBinding Information: 0 : BIND arg [System.Object[]] to parameter [ParamArray]
DEBUG: ParameterBinding Information: 0 :     Executing DATA GENERATION metadata: [System.Management.Automation.ArgumentTypeConverterAttribute]
DEBUG: ParameterBinding Information: 0 :         result returned from DATA GENERATION: System.Object[]
DEBUG: ParameterBinding Information: 0 :     Binding collection parameter ParamArray: argument type [Object[]], parameter type [System.String[]], collection type Array, element type [System.String], no coerceElementType
DEBUG: ParameterBinding Information: 0 :     Arg is IList with 0 elements
DEBUG: ParameterBinding Information: 0 :     Creating array with element type [System.String] and 0 elements
DEBUG: ParameterBinding Information: 0 :     Argument type System.Object[] is IList
DEBUG: ParameterBinding Information: 0 :     BIND arg [System.String[]] to param [ParamArray] SUCCESSFUL

Here, on the other hand, we see PowerShell creating a new array (third to last debug statement) and then binding that to $ParamArray.

Upvotes: 6

Related Questions