Search code examples
powershellreferencehashtable

Creating a reference to a hash table element


So, I'm trying to create a tree-type variable that I could use for data navigation. I've ran into an issue while trying to use reference variables on hash tables in PowerShell. Consider the following code:

$Tree = @{ TextValue = "main"; Children = @() }
$Item = @{ TextValue = "sub"; Children = @() }
$Pointer = [ref] $Tree.Children
$Pointer.Value += $Item
$Tree

When checking reference variable $Pointer, it shows appropriate values, but main variable $Tree is not affected. Is there no way to create references to a hash table element in PowerShell, and I'll have to switch to a 2-dimensional array?

Edit with more info:

I've accepted Mathias' answer, as using List looks like exactly what I need, but there's a little more clarity needed on how arrays and references interact. Try this code:

$Tree1 = @()
$Pointer = $Tree1
$Pointer += 1
Write-Host "tree1 is " $Tree1
$Tree2 = @()
$Pointer = [ref] $Tree2
$Pointer.Value += 1
Write-Host "tree2 is " $Tree2

As you can see from the output, it is possible to get a reference to an array and then modify the size of the array via that reference. I thought it would also work if an array is an element of another array or a hash table, but it does not. PowerShell seems to handle those differently.


Solution

  • I suspect this to be an unfortunate side-effect of the way += works on arrays.

    When you use += on a fixed-size array, PowerShell replaces the original array with a new (and bigger) array. We can verify that $Pointer.Value no longer references the same array with GetHashCode():

    PS C:\> $Tree = @{ Children = @() }
    PS C:\> $Pointer = [ref]$Tree.Children
    PS C:\> $Tree.Children.GetHashCode() -eq $Pointer.Value.GetHashCode()
    True
    PS C:\> $Pointer.Value += "Anything"
    PS C:\> $Tree.Children.GetHashCode() -eq $Pointer.Value.GetHashCode()
    False
    

    One way of going about this is to avoid using @() and +=.

    You could use a List type instead:

    $Tree = @{ TextValue = "main"; Children = New-Object System.Collections.Generic.List[psobject] }
    $Item = @{ TextValue = "sub"; Children = New-Object System.Collections.Generic.List[psobject] }
    $Pointer = [ref] $Tree.Children
    $Pointer.Value.Add($Item)
    $Tree