К дополнить полезный ответ Матиаса Р. Йессена :
Действительно, любой массив имеет фиксированный размер и не может быть расширен вместо (@()
создает пустой [object[]]
массив).
+=
в PowerShell незаметно создает новый массив , с копией всех исходных элементов плюс новый (ие), и назначает что на LHS.
Использование [ref]
бессмысленно , поскольку одного $Pointer = $Tree.Children
достаточно для копирования ссылки в массив, хранящийся в $Tree.Children
.
См. Нижний раздел для обсуждения надлежащего использования [ref]
.
Таким образом, и $Tree.Children
и $Pointer
будут содержать ссылку на один и тот же массив, как это делает $Pointer.Value
в подходе, основанном на [ref]
.
Поскольку +=
создает новый массив, однако, что бы ни было на LHS - будь то $Pointer.Value
или без [ref]
, просто $Pointer
- просто получает новое ссылка на массив new , тогда как $Tree.Children
все еще указывает на старый.
Вы можете проверить это, используя прямой способ определить, указывают ли две переменные или выражения на один и тот же экземпляр ссылочного типа (то есть все коллекции):
PS> [object]::ReferenceEquals($Pointer.Value, $Tree.Children)
False
Обратите внимание, что [object]::ReferenceEquals()
применимо только к ссылочным типам , а не типам значений - переменные, содержащие последние значения хранилища напрямую вместо ссылки данные хранятся в другом месте .
Подход Матиаса решает вашу проблему, используя экземпляр [List`1]
вместо массива, который может быть расширен на месте его методом .Add()
, так что ссылка сохраненный в $Pointer[.Value]
никогда не нуждается в изменении и продолжает ссылаться на тот же список, что и $Tree.Children
.
Относительно вашего последующего вопроса: соответствующее использование [ref]
:
$Tree2 = @()
$Pointer = [ref] $Tree2
В этом случае , поскольку [ref]
применяется к переменной - как и было задумано - создает эффективный псевдоним переменной : $Pointer.Value
сохраняет , указывая на то, что содержит $Tree2
, даже если для $Tree2
позже назначены разные данные (независимо от того, являются ли эти данные экземпляром типа значения или ссылочного типа):
PS> $Tree2 = 'Now I am a string.'; $Pointer.Value
Now I am a string.
Также обратите внимание, что более типичный вариант использования [ref]
- передача переменных в функции в виде по ссылке параметров.
PS> function foo { param([ref] $vRef) ++$vRef.Value }; $v=1; foo ([ref] $v); $v
2 # value of $v was incremented via $vRef.Value
В отличие от этого вы не можете использовать [ref]
для создания такой постоянной косвенной ссылки на данные , такой как свойство объекта , содержащего в переменной, и использование [ref]
там по существу бессмысленно:
$Tree2 = @{ prop = 'initial val' }
$Pointer = [ref] $Tree2.prop # [ref] is pointless here
Позднее изменение $Tree2.prop
означает , а не , отраженное в $Pointer.Value
, поскольку $Pointer.Value
статически относится к ссылке , первоначально , хранящейся в $Tree2.prop
:
PS> $Tree2.prop = 'later val'; $Pointer.Value
initial val # $Pointer.Value still points to the *original* data
PowerShell должен, вероятно, предотвращать использование [ref]
с чем-либо, что не является переменной - см. эту проблему GitHub .