我正在制作PowerShell脚本,遇到一个奇怪的问题(至少对我的世界观是这样的:))这是带有$ 1属性和一些整数值的对象$ Source:
$Source
Priority
--------
43.37
26.51
23.69
6.43
我想创建一个新变量并将$ Source的内容复制到其中:
$ChangedSource = $Source
好的,现在我想稍微更改$ ChangedSource的值,而不会影响$ Source:
$ChangedSource | % {$_.Priority = 100}
所以,让我们检查一下它是否有效:
$ChangedSource
Priority
--------
100
100
100
100
成功了!但是,请确保$ Source不受此更改的影响:
$Source
Priority
--------
100
100
100
100
等等,什么?
有人可以向我解释如果我更改$ ChangedSource为什么$ Source会更改吗? $ ChangedSource仅仅是对$ Source的引用吗?如果是这样,如何将$ ChangedSource与$ Source分离?
答案 0 :(得分:3)
您对$ChangedSource
只是对$Source
对象的引用是正确的。
对于您想要的内容,只需执行
$Source
对象
$ChangedSource = $Source | Select-Object *
示例:
$Source= [PsCustomObject]@{'Priority' = 43.37},
[PsCustomObject]@{'Priority' = 26.51},
[PsCustomObject]@{'Priority' = 23.69},
[PsCustomObject]@{'Priority' = 6.43}
$ChangedSource = $Source | Select-Object *
$ChangedSource | ForEach-Object {$_.Priority = 100}
Write-Host '$Source' -ForegroundColor Yellow
$Source | Format-Table
Write-Host '$ChangedSource' -ForegroundColor Yellow
$ChangedSource | Format-Table
输出:
$Source
Priority
--------
43.37
26.51
23.69
6.43
$ChangedSource
Priority
--------
100
100
100
100
这有效,因为优先级值只是数字。
但是,如果$source
对象包含其他对象,并且您想将此对象克隆到另一个对象中,则最终仍将引用源和副本中的相同对象。
如果您希望能够在保持源代码完好无损的情况下操作副本,则需要“深度克隆”源代码对象。
为此,您可以使用以下功能:
function Clone-Object ([object]$obj, [switch]$DeepClone) {
if ($DeepClone) {
# create a deep-clone of an object
$ms = New-Object System.IO.MemoryStream
$bf = New-Object System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
$bf.Serialize($ms, $obj)
$ms.Position = 0
$clone = $bf.Deserialize($ms)
$ms.Close()
}
else {
# create a shallow copy of same type
$clone = New-Object -TypeName $($obj.GetType().FullName)
foreach ($pair in $obj.GetEnumerator()) { $clone[$pair.Key] = $pair.Value }
}
return $clone
}
如mklement0所述,以上功能有局限性。
这里有一个新版本(希望)做得更好。
使用-DeepClone
开关,如果源对象类型属性未设置'Serializable'标志,则该函数现在尝试使用[System.Management.Automation.PSSerializer]::Serialize()
克隆对象。< br />
如果同样失败,则会写出错误。
没有-DeepClone
开关,首先进行测试以确保源对象实现IEnumerable接口。如果是这种情况,它将尝试创建一个浅表副本以返回相同类型的对象。
否则,将使用$clone = $obj | Select-Object *
创建对象的副本,该副本具有源对象的属性,但类型为不同。
否则,它会尝试创建一个浅表副本以返回相同类型的对象。
请随时进行改进。
function Clone-Object ([object]$obj, [switch]$DeepClone) {
if ($DeepClone) {
# create a deep-clone of an object
# test if the object implements the IsSerializable Interface
if ([bool]($obj.GetType().IsSerializable)) { # or: if ([bool]($obj.GetType().Attributes -band 'Serializable')) {
$ms = New-Object System.IO.MemoryStream
$bf = New-Object System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
$bf.Serialize($ms, $obj)
$ms.Position = 0
$clone = $bf.Deserialize($ms)
$ms.Close()
}
else {
# try PSSerializer that serializes to CliXml
# source: https://stackoverflow.com/a/32854619/9898643
try {
$clixml = [System.Management.Automation.PSSerializer]::Serialize($obj, 100)
$clone = [System.Management.Automation.PSSerializer]::Deserialize($clixml)
}
catch {
Write-Error "Could not Deep-Clone object of type $($obj.GetType().FullName)"
}
}
}
else {
# create a shallow copy of the same type
# if the object has a Clone() method
if ($obj -is [System.ICloneable]) {
$clone = $obj.Clone()
}
# test if the object implements the IEnumerable Interface
elseif ($obj -is [System.Collections.IEnumerable]) {
try {
$clone = New-Object -TypeName $($obj.GetType().FullName) -ErrorAction Stop
foreach ($pair in $obj.GetEnumerator()) { $clone[$pair.Key] = $pair.Value }
}
catch {
Write-Error "Could not Clone object of type $($obj.GetType().FullName)"
}
}
else {
# this returns an object with the properties copied,
# but it is NOT OF THE SAME TYPE as the source object
$clone = $obj | Select-Object *
}
}
return $clone
}