对var2的更改也会更改var1,而var1是从var1派生的

时间:2019-09-07 11:47:32

标签: powershell

我正在制作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分离?

1 个答案:

答案 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
}