【问题标题】:How to create new clone instance of PSObject object如何创建 PSObject 对象的新克隆实例
【发布时间】:2012-03-06 10:12:45
【问题描述】:

我想创建自定义 PSObject 的新实例。我有一个 Button 对象创建为 PSObject 并且我想创建新对象 Button2 它具有与 Button 相同的成员,但是我找不到如何克隆原始对象而不使其在原始对象中引用的方法(如果我更改Button2 中的一个属性,它也会在 Button 中更改)。有没有办法通过一些 Clone() 方法像哈希表和数组一样做到这一点?

【问题讨论】:

  • 您没有指定克隆是否应该是浅的,这很关键(下面的答案在此行为中有所不同)-we-are.bookmyshow.com/…

标签: powershell


【解决方案1】:

最简单的方法是使用PsObjectCopy 方法==> $o2 = $o1.PsObject.Copy()

$o1 = New-Object -TypeName PsObject -Property @{
    Fld1 = 'Fld1';
    Fld2 = 'Fld2';
    Fld3 = 'Fld3'}

$o2 = $o1.PsObject.Copy()

$o2 | Add-Member -MemberType NoteProperty -Name Fld4 -Value 'Fld4'
$o2.Fld1 = 'Changed_Fld'

$o1 | Format-List
$o2 | Format-List

输出:

Fld3 : Fld3
Fld2 : Fld2
Fld1 : Fld1

Fld3 : Fld3
Fld2 : Fld2
Fld1 : Changed_Fld
Fld4 : Fld4

【讨论】:

【解决方案2】:

由于某种原因,PSObject.Copy() 不适用于所有对象类型。创建对象副本的另一种解决方案是将其转换为 Json 或从 Json 转换,然后将其保存在新变量中:

$CustomObject1 = [pscustomobject]@{a=1; b=2; c=3; d=4}
$CustomObject2 = $CustomObject1 | ConvertTo-Json -depth 100 | ConvertFrom-Json
$CustomObject2 | add-Member -Name "e" -Value "5" -MemberType noteproperty

$CustomObject1 | Format-List
$CustomObject2 | Format-List

【讨论】:

  • 这是迄今为止唯一可以深度克隆包含其他 psobject 的 psobject 的答案。
  • 注意由于一个错误,在某些版本的 powershell 中,如果数据中的任何地方都有换行符,则需要使用ConvertTo-Json -compress -depth 100 | ConvertFrom-Json(注意-compress)(可能其他字符会触发此操作)
【解决方案3】:

确实没有克隆方法!但是有意愿的地方……

$o = New-Object PsObject -Property @{ prop1='a' ; prop2='b' }
$o2 = New-Object PsObject
$o.psobject.properties | % {
    $o2 | Add-Member -MemberType $_.MemberType -Name $_.Name -Value $_.Value
}
$o.prop1 = 'newvalue'

$o
$o2

输出:

prop2     prop1                                                                 
-----     -----                                                                 
b         newvalue                                                              
b         a      

【讨论】:

  • 这很奇怪,我没有可用的克隆方法。看起来我创建对象的方式有问题。当我使用您的示例时,我有可用的克隆方法,但我先创建空对象,然后通过 Add-Member 添加所有成员。所以当我使用: $object = New-Object PSObject $object | Add-Member NoteProperty propVal "test" powershell 说 [System.Management.Automation.PSCustomObject] 不包含方法 Close。
  • @JosefNemec 很糟糕,我使用错误的语法来创建 PsObject。您是正确的,没有克隆方法。因此,要解决这个问题,您只需将所有原始属性复制到新对象即可。
  • 如果您将对象作为属性将不起作用,那么这些对象是引用,因此编辑它会编辑克隆以及原始对象
  • 添加成员很慢!
  • 请注意,这个副本会很浅(we-are.bookmyshow.com/…
【解决方案4】:

另一种可能性:

 $o1 = New-Object PsObject -Property @{ prop1='a' ; prop2='b' }
 $o2 = $o1 | select *
 $o2.prop1 = 'newvalue'
 $o1.prop1
 $o2.prop1
 a
 newvalue

【讨论】:

  • 似乎不起作用...我得到了两个新值。尝试调用GetHashCode(),它们对我来说指向同一个对象。
  • 不同的方法。效果更好。
  • 使用Select-Object的好方法。
  • 知道必须有比一次创建一个新对象一个属性更好的方法。
  • 我认为Select-Object 在内部做同样的事情。动态属性创建。使用这种方法,输入的代码肯定更少。
【解决方案5】:

这是一个带有隐藏 .psobject.copy() 的 [pscustomobject] 示例:

$a = [pscustomobject]@{message='hi'}
$a.message
hi

$b = $a.psobject.copy()
$b.message
hi

$a.message = 'there'
$a.message
there

$b.message
hi

【讨论】:

    【解决方案6】:

    从 PowerShell v5 开始,您可以使用 Class。 psobject.Copy() 的问题是,如果更新克隆对象,那么模板对象的引用属性也会更新。

    示例:

    function testTemplates
    {
        $PSCustomObjectTemplate = New-Object PSCustomObject -Property @{
            List1 = [System.Collections.Generic.List[string]]@()  # will be updated in template
            String1 = "value1" # will not be updated in template
            Bool1 = $false # will not be updated in template
        }
    
        $objectFromPSTemplate1 = $PSCustomObjectTemplate.psobject.Copy()
        $objectFromPSTemplate1.List1.Add("Value")
        $objectFromPSTemplate1.String1 = "value2" 
        $objectFromPSTemplate.Bool1 = $true 
    
        # $PSCustomObjectTemplate IS updated, so CANNOT be used as a clean template!
        $PSCustomObjectTemplate
    
        Class ClassTemplate {
            [System.Collections.Generic.List[string]]$List1 = @() # will not be updated in template
            [string]$String1 = "value1" # will not be updated in template
            [bool]$Bool1 = $false # will not be updated in template
        }
        $objectFromClassTemplate = [ClassTemplate]::new()
        $objectFromClassTemplate.List1.Add("Value")
        $objectFromClassTemplate.String1 = "value2"
        $objectFromClassTemplate.Bool1 = $true
    
        # $ClassTemplate IS NOT updated, so can be used as a clean template!
        [ClassTemplate]::new()
    }
    

    测试模板

    PS C:\Windows\system32> testTemplates
    
    List1   String1 Bool1
    -----   ------- -----
    {Value} value1  False       
    

    ->来自 PSCustomObject 的模板已更新(引用属性 -List1)

    List1   String1 Bool1
    -----   ------- -----
    {}      value1  False       
    

    -> 类中的模板是安全的

    【讨论】:

      【解决方案7】:

      我发现更好的方法是使用 ConvertTo-Json 和 ConvertFrom-Json。 呃 - 假设你想克隆一个对象 $toBeClonedObject,只需运行下面的代码来克隆。

      $clonedObject = $toBeClonedObject | ConvertTo-Json | ConvertFrom-Json
      

      【讨论】:

      • 这是下面 TeraFlux 答案的副本,并且容易受到相同问题的影响: ConvertTo-Json 的默认深度为 2(对于深度克隆不是很有用),如果任何值包含换行
      【解决方案8】:

      这通常对我有用:

      $Source = [PSCustomObject]@{ Value = 'Test' };
      $Copy = ($Source | ConvertTo-Json) | ConvertFrom-Json;
      

      【讨论】:

        【解决方案9】:

        把它放在一个实用程序类中或在你当前的部分中定义它

        function clone($obj)
        {
            $newobj = New-Object PsObject
            $obj.psobject.Properties | % {Add-Member -MemberType NoteProperty -InputObject $newobj -Name $_.Name -Value $_.Value}
            return $newobj
        }
        

        用法:

        $clonedobj = clone $obj

        【讨论】:

          【解决方案10】:

          根据@TeraFlux 的回答,这里有一个函数将对多个对象进行深度复制并接受管道输入。

          注意,它利用默认深度为 100 的 json 转换,这使其存在一些弱点

          • 对于深度或复杂对象,或具有昂贵(缓慢)伪属性的对象(methods 假装是在请求时动态计算的属性)会很慢
            • 虽然它仍然应该比 Add-Member 方法快,因为繁重的工作是通过编译函数完成的
          • 任何无法存储在JSON 中的内容都可能被损坏或遗留(方法将是此类错误的主要候选对象)
            • 尽管任何可以安全通过此过程的对象都应该是可保存的,能够安全存储(用于恢复)或出口用于运输

          我会对处理这些问题的任何警告或改进感兴趣

          function Clone-Object {
              [CmdletBinding()]
              Param (
                  [Parameter(ValueFromPipeline)] [object[]]$objects,
                  [Parameter()] [int] $depth = 100
              )
              $clones = foreach( $object in $objects ){
                  $object `
                  | ConvertTo-Json `
                      -Compress `
                      -depth $depth `
                  | ConvertFrom-Json
              }
              return $clones
          }
          

          这里有一些非常基本的单元测试

          $testClone = {
              $test1 = $null
              $test2 = $null
              $test3 = $null
          
              $Test1 = [psCustomObject]@{a=1; b=2; c=3; d=4}
              $Test2 = $Test1 | ConvertTo-Json -depth 100 | ConvertFrom-Json
              $Test2 | add-Member -Name "e" -Value "5" -MemberType noteproperty
              $Test3 = $test2 | Clone-Object
              $Test3 | add-Member -Name "f" -Value "6" -MemberType noteproperty
              $Test1.a = 7
              $Test2.a = 8
          
              #$Expected0 = [psCustomObject]@{a=1; b=2; c=3; d=4}
              $Expected1 = [pscustomobject]@{a=7; b=2; c=3; d=4}
              $Expected2 = [pscustomobject]@{a=8; b=2; c=3; d=4; e=5}
              $Expected3 = [pscustomobject]@{a=1; b=2; c=3; d=4; e=5; f=6}
          
              $results1 = @(); $results1+=$test1; $results1+=$expected1
              $results2 = @(); $results2+=$test2; $results2+=$expected2
              $results3 = @(); $results3+=$test3; $results3+=$expected3
              $results1 | Format-Table # if these don't match then its probably passing references (copy not clone)
              $results2 | Format-Table # if these don't match the core approach is incorrect
              $results3 | Format-Table # if these don't match the function didn't work
          
          }
          &$testClone
          

          【讨论】:

            【解决方案11】:

            另一种选择:

            function Copy-Object($Object) {
                $copy = @()
                $Object.ForEach({
                    $currentObject = $_
                    $currentObjectCopy = New-Object $currentObject.GetType().Name
                    $currentObjectCopy.psobject.Properties.ForEach({
                        $_.Value = $currentObject.psobject.Properties[($_.Name)].Value
                    })
                    $copy += $currentObjectCopy
                })
                return $copy
            }
            

            测试对象:

            class TestObjectA {
                [string]$g
                [int[]]$h
                
                [string]getJ(){
                    return 'j'
                }
            }
            
            class TestObjectB {
                [string]$a
                [int]$b
                [hashtable]$c
                [TestObjectA[]]$d
            
                [string]getI(){
                    return 'i'
                }
            } 
            

            测试:

            $b = New-Object -TypeName TestObjectB -Property @{
                a = 'value a'
                b = 2
                c = @{ e = 'value e'; f = 3 }
                d = New-Object -TypeName TestObjectA -Property @{
                    g = 'value g'
                    h = @(4,5,6)
                }
            }
            
            $bCopy = Copy-Object $b
            
            # test with simple comparison
            -not $(Compare-Object $b $bCopy)
            True
            
            # test json deep conversion output
            $bJson = $b | ConvertTo-Json -Depth 10
            $bCopyJson = $bCopy | ConvertTo-Json -Depth 10
            -not $(Compare-Object $bJson $bCopyJson)
            True
            
            # test methods are intact
            $bCopy.getI()
            i
            $bCopy.d.GetJ()
            j    
            
            # test objects are seperate instances
            $bCopy.b = 3
            $b.b
            2
            $bCopy.b
            3
            

            【讨论】:

            • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
            猜你喜欢
            • 2011-08-25
            • 1970-01-01
            • 2011-05-06
            • 2017-06-19
            • 2013-04-23
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-06-19
            相关资源
            最近更新 更多