我正在制作一个 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 中分离出来?
最佳答案
你是对的 $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
object 包含其他对象,并且您想将其克隆到另一个对象中,您仍然会引用源和副本中的相同对象。如果您希望能够在保持源完整的同时操作副本,则需要“深度克隆”源对象。
为此,您可以使用以下功能:
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
开关 ,该函数现在尝试使用 [System.Management.Automation.PSSerializer]::Serialize()
克隆对象如果源对象类型属性没有设置“Serializable”标志。如果这也失败了,则会写出一个错误。
没有
-DeepClone
开关 ,首先进行测试以确保源对象实现 IEnumerable 接口(interface)。如果是这种情况,它会尝试创建一个浅克隆以返回相同类型的对象。否则,使用
$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
}
关于powershell - 对 var2 的更改也会更改 var1 派生自 var1,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57833377/