powershell - 对 var2 的更改也会更改 var1 派生自 var1

标签 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 中分离出来?

最佳答案

你是对的 $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/

相关文章:

powershell - Bamboo Powershell任务在首次运行后失败

powershell - Pester 示例脚本在 Windows 10 上获取 "-Be is not a valid Should operator",在 Ubuntu 上运行良好

powershell - 如何使用 PowerShell 检查 csv 列中的空行

powershell - Powershell中快速简单的二进制连接文件

Powershell Hyper-V 脚本不起作用? + 自动安装 Windows

bash - 如何在错误时停止Powershell脚本?

powershell - 为什么单元素哈希表在 powershell 中的行为不同?

powershell - 获取名称文件夹的一部分

arrays - Powershell:将一个数组添加为另一个数组的成员

windows - 如何在 Powershell 中创建文件名中包含特殊字符的文件的 Windows 快捷方式?