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.
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