Powershell foreach-Loop: невозможно установить элементы массива

В данный момент я полностью запутался:

Я хотел решить проблему передачи массива функции по значению вместо ссылки и нашел следующее решение: Передается ли Arraylist функциям по ссылке в PowerShell.

Метод .Clone () работает так, как ожидалось:

function testlocal {
   param ([collections.arraylist]$local)
   $local = $local.Clone()
   $local[0] = 10

   return $local
 }

 $local = [collections.arraylist](1,2,3)

 'Testing function arraylist'    
 $copyOfLocal = testlocal $local
 $copyOfLocal

 'Testing local arraylist'
 $local

Выход:

Testing function arraylist
10
2
3

Testing local arraylist
1
2
3

Но теперь мне нужно обработать элементы массива в цикле foreach. Тогда происходит то, что массив не изменяется с помощью цикла foreach (???). Я затрудняюсь понять это, несмотря на множество исследований. Не могли бы вы объяснить мне, что происходит за кулисами и как я могу этого избежать? Мне нужно изменить копию исходного массива в цикле foreach функции. В моем реальном сценарии массив состоит из настраиваемых объектов PSObject, но поведение такое же.

function testlocal {
   param ([collections.arraylist]$local)
   $local = $local.Clone()
   $local[0] = 10

   foreach ($item in $local) {
      $item = 100
   }

   return $local
 }

 $local = [collections.arraylist](1,2,3)

 'Testing function arraylist'    
 $copyOfLocal = testlocal $local
 $copyOfLocal

 'Testing local arraylist'
 $local

Цикл foreach не изменяет вывод:

Testing function arraylist
10
2
3

Testing local arraylist
1
2
3

ОБНОВЛЕНИЕ 2016-12-14 Совет с циклом for работает, но оказывается, что при использовании объектов все клонирование снова разваливается:

 function testlocal {
   param ([collections.arraylist]$local)
   $local = $local.Clone()

 for($i = 0; $i -lt $local.Count; $i++){ 

   $local[$i].Hostname = "newname" 
   }
   return $local

 }

$target1 = New-Object -TypeName PSObject
$target1 | Add-Member -MemberType NoteProperty -Name "Hostname" -Value "host1"

$target2 = New-Object -TypeName PSObject
$target2 | Add-Member -MemberType NoteProperty -Name "Hostname" -Value "host2"


 $local = [collections.arraylist]($target1,$target2)

 'Testing function arraylist'    
 $copyOfLocal = testlocal $local
 $copyOfLocal | ft

 'Testing local arraylist'
 $local | ft

Выход:

Testing function arraylist

Hostname
--------
newname 
newname 

Testing local arraylist

Hostname
--------
newname 
newname 

Внезапно я снова возвращаюсь к переходу по ссылке. Это сводит меня с ума! Пожалуйста помоги!


person Damartala    schedule 02.12.2016    source источник
comment
Используйте цикл for: for($i = 0; $i -lt $local.Count; $i++){ $local[$i] = 100 }   -  person Mathias R. Jessen    schedule 02.12.2016
comment
Спасибо, но это не работает с объектами, см. Мое обновление внизу моего исходного сообщения. Есть еще идеи, как с этим бороться?   -  person Damartala    schedule 15.12.2016


Ответы (1)


Когда вы перечисляете массив, вызывая foreach, вы получаете копии содержимого, и любые изменения будут отменены. Как упомянул Матиас Джессен, вы можете использовать цикл for для внесения изменений в arraylist.

РЕДАКТИРОВАТЬ 2016-12-16 Хорошо, я изучил это, и метод клонирования arraylist не работает, как вы, и я думал, что это так. Согласно MSDN, он возвращает неглубокую копию, то есть «копирует только элементы коллекции, независимо от того, являются ли они ссылочными типами или типами значений, но не копирует объекты, на которые ссылаются ссылки». Вы и я предполагали, что этот клон произведет то, что называется «глубокой копией».

В вашем исходном примере с использованием базового типа данных Int32 глубокая и неглубокая копии - это одно и то же, тогда как в вашем обновленном примере с использованием PSObjects это не так, и копируются только ссылки. В этом потоке можно найти метод глубокого клонирования объектов.

Я не нашел документации по этому поводу, но похоже, что foreach также будет делать мелкие копии:

$object1 = New-Object -TypeName PSObject -Property @{Hostname="host1"}
$object2 = New-Object -TypeName PSObject -Property @{Hostname="host2"}
$array_of_objects = [collections.arraylist]($object1,$object2)

# Looping through basic values, call by value
foreach ($string in $array_of_objects.Hostname)
{
    $string = "newname"
}
$array_of_objects.Hostname

# Looping through objects, call by reference
foreach ($object in $array_of_objects)
{
    $object.Hostname = "newname"
}
$array_of_objects.Hostname

Надеюсь это поможет.

person josges    schedule 02.12.2016
comment
Привет, спасибо за быстрые ответы, @Mathias R. Jessen и josges. Извините, но я не мог следить за этим раньше. Цикл for работает должным образом, но не в том случае, если я использую объекты ?? Я добавил эту проблему в свой исходный пост, потому что не смог вписать ее в комментарий. Ты тоже знаешь об этом? Спасибо! - person Damartala; 14.12.2016