I have a PowerShell multidimensional object called $foo
.
If I want to edit one specific child object, I can use :
$foo[15].Name = 'Something'
But I want to edit that child object without editing the parent object. If I use :
$bar = $foo[15]
$bar.Name = 'Something'
This still edits the $foo
parent object.
Why are $foo
and $bar
linked ?
Why, when I edit one, does it edit the other ?
Is there anyway to bypass this, so I can edit the subobject without altering the original, parent object ?
Why, when I edit one, does it edit the other ?
Because you're dealing with a reference type, when you do:
$bar = $foo[15]
Then $foo[15]
and $bar
have the same reference, you can tell by using [object]::ReferenceEquals($objA, $objB)
, for example:
$foo = @(
[pscustomobject]@{ Name = 'Something' }
[pscustomobject]@{ Name = 'Something' }
)
$bar = $foo[1]
[object]::ReferenceEquals($foo[1], $bar) # True
Is there anyway to bypass this, so I can edit the sub object without altering the original, parent object ?
Yes there is and the method really depends on the type itself and its depth or complexity, for instance for these simple pscustomobject
s provided above, they have only one level deep, for them specifically we could use the .Copy()
method, this would produce a shallow copy of the object but in this case it would suffice:
$bar = $foo[1]
[object]::ReferenceEquals($foo[1], $bar) # True
$bar = $foo[1].PSObject.Copy()
[object]::ReferenceEquals($foo[1], $bar) # False
Now $bar
can be safely updated without updating it's original reference. Other reference types may provide their own cloning implementation, i.e. Hash tables have their own .Clone() Method
. For more complex objects or objects with greater depth, the way around this is via serialization and deserialization, the 2 most commonly used methods in PowerShell are Json and CliXml:
$bar = $foo[1]
[object]::ReferenceEquals($foo[1], $bar) # True
# To and from Json
$bar = $foo[1] | ConvertTo-Json -Depth 99 | ConvertFrom-Json
[object]::ReferenceEquals($foo[1], $bar) # False
# To and from CliXml
$bar = [System.Management.Automation.PSSerializer]::Deserialize(
[System.Management.Automation.PSSerializer]::Serialize($foo[1], 99))
[object]::ReferenceEquals($foo[1], $bar) # False