1

I need some assistance please, to understand behavior of arrays and powershell loops. I have stumbled upon a strange array behavior, here is the issue and sample script:

# I create simple array
$item2 = New-Object Collections.ArrayList

# getting some sample data for array
$allitems = Get-ChildItem -Path "c:\" | select name,Attributes

#running loop
foreach ($item in $allitems){

   # outputing iriginal array item
   Write-Host $item

   # addind to new array
   $item2.Add($item)| Out-Null

   # here is the issue I have - after I add member to my $item2 array
   # it is replicated to also $allitems, I don't need this behavior
   # to happen. Also I don't understand why it changes the original
   # $item  and $allitems. Am I creating array copy incorrectly ?

   $item2[-1] | Add-Member -MemberType NoteProperty -name 'testproperty' -value 'testvalue'
   write-host "$($item2[-1]) mod"
   Write-Host $item
}

result of the script is this :

@{Name=rollback2.puc; Attributes=Archive} -> original entry
@{Name=rollback2.puc; Attributes=Archive; testproperty=testvalue} mod -> modified entry
@{Name=rollback2.puc; Attributes=Archive; testproperty=testvalue} -> original entry modified , why ?

Any help appreciated. Thanks

1
  • 1
    Call Get-ChildItem -Path twice Commented Mar 15, 2022 at 16:57

1 Answer 1

2

Most types of objects in .NET are reference-type objects, meaning that what you have stored in $item is not the object itself, but a reference to it.

When you then call $item2.Add($item), the reference is copied to $item2, but both the array list and the variable still point back to the exact same object/memory address, and PowerShell will therefore "remember" the added property regardless of which copy of the object reference you use.

In this particular case, you can work around it and create 2 sets of objects by calling Get-ChildItem twice:

$item2 = New-Object Collections.ArrayList

$allitems = Get-ChildItem -Path "c:\" | select name,Attributes

foreach ($item in Get-ChildItem -Path "c:\" | select name,Attributes){
  Write-Host $item

  $item2.Add($item)| Out-Null

  $item2[-1] | Add-Member -MemberType NoteProperty -name 'testproperty' -value 'testvalue'

  # the objects stored in $allitems will have been unaffected by the call above
}

Or by calling Get-Item to create a new object to represent the file just before adding to the array list:

$item2 = New-Object Collections.ArrayList

$allitems = Get-ChildItem -Path "c:\" | select name,Attributes

#running loop
foreach ($item in $allitems){
  Write-Host $item

  # Create a new FileInfo object by calling `Get-Item`
  $itemCopy = $item | Get-Item
  $item2.Add($itemCopy) | Out-Null

  $item2[-1] | Add-Member -MemberType NoteProperty -name 'testproperty' -value 'testvalue'

  # $item2[-1] has been modified, but $item has not
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you very much for the help ! Your answer helped me understand little more about object references and was able to find that proper cloning of arrays/objects in PowerShell can be done with serialisation also known as deep cloning. Running commands twice for different objects works too, only caveat if you have large items or something that takes long time , running twice not always convenient and can have different results for each object ( due to changes in between the runs).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.