2015-06-04 2 views
1

я работал на что-то сегодня и во время тестирования я заметил очень специфический вопросСвой вопрос с Powershell Hashtable и массив

$arry = @() 
$Msg = @{Body="This is a Sample Message";} 

[email protected]{} 
$Msg.BrokerProperties.Label= "Msg1" 

$arry += $Msg 
$arry | ConvertTo-Json # 1st Result 

[email protected]{} 
$Msg.BrokerProperties.Label= "Msg2" 

$arry += $Msg 

$arry | ConvertTo-Json 

1-й результат $arry | ConvertTo-Json как ниже

{ «Body «: "Это образец сообщения", "BrokerProperties": { "Этикетка": "msg1" }}

2-й результат $arry | ConvertTo-Json как ниже

[ { "Body": "Это пример сообщения", "BrokerProperties": { "Этикетка": "msg2" } }, { "Body": "Это образец сообщения", "BrokerProperties": { "Этикетка": "msg2" } }]

То, что я думал, это когда я установил $Msg.BrokerProperties.Label= "Msg2" для второго раза, тогда это произвело бы только 2-ая хэш-таблица в массиве. но очень интересно, что собственность вводится даже на 1-ю хэш-таблицу.

Может кто-нибудь объяснить это поведение?

Я фактически делал это в цикле для подготовки полезной нагрузки JSON для отправки на API вызов, поэтому я lookign способа обновить метки для каждого внутреннего внутри объекта JSon

+0

Изменяет ли переменная операцию добавления массива на $ arry + =, $ Msg? – arco444

+0

Нет, теперь я начинаю понимать, что массивы являются ссылочными типами (а не типами значений) –

+0

Когда я пытаюсь выполнить строчную строчку ниже, свойство Label уже находится в $ msg, когда я вывожу его $ arry = @() $ Msg = @ {Body = "Это примерное сообщение"; BrokerProperties = @ {Priority = "Medium"}} $ Msg1 = $ Msg $ Prp = $ Msg1.Get_Item ("BrokerProperties") $ Prp.Add ("Ярлык" , "Msg1") $ arry + = $ Msg1 $ Msg2 = $ Msg $ Msg2 | ConvertTo-Json –

ответ

2

Hashtables передается по ссылка, а не по значению.

Чтобы создать новую хеш-таблицу из существующего Hashtable, используйте Clone():

$arry += [hashtable]$Msg.Clone() 

Имейте в виду, что это создает мелкий клон, поэтому если у вас есть вложенные HashTables, внутренние большинство записей по-прежнему будут ссылочные типы, и в зависимости от обстоятельств вы могли бы написать свою собственную функцию клонирования.

+0

точно, что я обнаружил только сейчас после дальнейшего копания. Спасибо –

+0

Ninja'd меня через минуту. К сожалению. – Vesper

+0

@Vesper Вот как я роняю ;-) –

3

Очевидно, массив Powershell содержит только указатели объектов, если к ним добавлена ​​переменная, поэтому вы просто добавили две ссылки на один объект в свой arry. Чтобы уточнить, после вашей операции это заявление:

$arry[0] -eq $arry[1] 

вернет true. Чтобы исправить это, вы должны использовать функцию clone(), чтобы создать совершенно новый и независимый объект от $Msg, так что любые изменения не изменят объект, который вы сохранили в своем массиве.

$arry = @() 
$Msg = @{Body="This is a Sample Message";} 

[email protected]{} 
$Msg.BrokerProperties.Label= "Msg1" 

$arry += $Msg 
$arry | ConvertTo-Json # 1st Result 

$Msg=$Msg.clone() # create a new copy 
$Msg.BrokerProperties.Label= "Msg2" # alter new copy 

$arry += $Msg 

$arry | ConvertTo-Json # get two different tables in array 

EDIT: В вашем случае, вы должны клонировать BrokerProperties, а также, потому что это также Хеш и клонирование верхнего уровня $Msg результатов в двух различных объектах, которые содержат ссылки на одного вложенных хеш-таблица. Итак, чтобы получить совершенно другой объект, вы должны сделать deep copy вашей хэш-таблицы.

$arry = @() 
$Msg = @{Body="This is a Sample Message";} 

[email protected]{} 
$Msg.BrokerProperties.Label= "Msg1" 

$arry += $Msg 
$arry | ConvertTo-Json # 1st Result 

# $Msg=$Msg.clone() this is not enough!!! 
$memStream = new-object IO.MemoryStream 
$formatter = new-object Runtime.Serialization.Formatters.Binary.BinaryFormatter 
$formatter.Serialize($memStream,$Msg) # serialization makes a string out of an object's structure 
$memStream.Position=0 
$Msg = $formatter.Deserialize($memStream) # deserialization makes a completely different object 
$Msg.BrokerProperties.Label= "Msg2" # and now changing the nested hash table won't change the old one. 

$arry += $Msg 

$arry | ConvertTo-Json # get two different tables in array 

На стороне записки: Если вы создаете несколько объектов, основанных на той же «шаблон» объект, вам не нужно сериализовать все время, просто держать ссылки на $memStream и $formatter (один поток памяти на один объект до глубокой копии, один форматировщик на сценарий) и просто вызывайте $memstream.position=0; $formatter.deserialize($memstream), чтобы получить еще одну подготовленную копию того же объекта, который был ранее сериализован.

+0

Хорошая рутина глубокой копии –

Смежные вопросы