【问题标题】:Peculiar issue with powershell Hashtable and arraypowershell Hashtable 和数组的特殊问题
【发布时间】:2015-06-04 21:55:45
【问题描述】:

我今天正在做一些事情,在测试过程中我发现了一个非常奇怪的问题

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

$Msg.BrokerProperties=@{}
$Msg.BrokerProperties.Label= "Msg1"

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

$Msg.BrokerProperties=@{}
$Msg.BrokerProperties.Label= "Msg2"

$arry += $Msg

$arry | ConvertTo-Json

$arry | ConvertTo-Json的第一个结果如下

{ "Body": "这是一个示例消息", “经纪人属性”:{ “标签”:“Msg1” } }

$arry | ConvertTo-Json的第二个结果如下

[ { "Body": "这是一个示例消息", “经纪人属性”:{ “标签”:“Msg2” } }, { "Body": "这是一个示例消息", “经纪人属性”:{ “标签”:“Msg2” } } ]

我认为会发生的是,当我第二次设置 $Msg.BrokerProperties.Label= "Msg2" 时,它只会影响数组中的 2nd hashtable。但非常有趣的是,该属性甚至被注入到第一个哈希表中。

有人可以解释一下这种行为吗?

我实际上是在循环中执行此操作以准备 JSON 有效负载以发送到 API 调用,因此我正在寻找一种方法来更新 json 对象中每个内部的标签

【问题讨论】:

  • 将数组追加操作更改为$arry += ,$Msg 会改变行为吗?
  • 不,我现在开始意识到数组是引用类型(不是值类型)
  • 当我逐行尝试以下内容时,Label 属性在我输出时已经在 $msg 中 $arry = @() $Msg = @{Body="This is a Sample Message";BrokerProperties =@{Priority="Medium"}} $Msg1 = $Msg $Prp = $Msg1.Get_Item("BrokerProperties") $Prp.Add("Label","Msg1") $arry += $Msg1 $Msg2 = $消息 $Msg2 | ConvertTo-Json

标签: powershell


【解决方案1】:

显然,如果向其中添加变量,则 Powershell 数组仅包含对象指针,因此您刚刚将两个对单个对象的引用添加到您的 arry 中。为了澄清,在你的操作之后这个声明:

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

将返回 true。作为补救措施,您应该使用clone() 函数从$Msg 创建一个全新且独立的对象,这样任何修改都不会改变您存储在数组中的对象。

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

$Msg.BrokerProperties=@{}
$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

编辑:在您的情况下,您还必须克隆 BrokerProperties,因为它也是一个哈希表,并且克隆上层 $Msg 会生成两个不同的对象,其中包含指向单个嵌套哈希表。所以,为了接收完全不同的对象,你必须对你的哈希表做一个deep copy。

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

$Msg.BrokerProperties=@{}
$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) 即可接收先前序列化的同一对象的另一个准备好的副本。

【讨论】:

  • 不错的深拷贝程序
【解决方案2】:

哈希表通过引用而不是按值传递。

要从现有哈希表创建新哈希表,请使用Clone():

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

请注意,这会创建一个浅克隆,因此如果您有嵌套的哈希表,最里面的条目仍然是引用类型,并且根据具体情况,您可能需要编写自己的克隆函数

【讨论】:

  • 正是,我在进一步挖掘后才发现。谢谢
  • 忍者领先我一分钟。哎呀。
  • @Vesper 我就是这样滚动的 ;-)
  • @MathiasR.Jessen 不过,我在这个问题上打败了你,因为你的回答只是部分正确,请检查我的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-06-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-21
  • 2021-10-11
  • 2022-01-13
相关资源
最近更新 更多