How to create new clone instance of PSObject object

前端 未结 9 663
野趣味
野趣味 2020-12-09 02:20

I want to create new instance of my custom PSObject. I have a Button object created as PSObject and I want to create new object Button2 which has the same members as Button

相关标签:
9条回答
  • 2020-12-09 03:14

    Starting from PowerShell v5, you can use Class. The problem with psobject.Copy() is, if you update the cloned object, then your template object's referenced properties will be also updated.

    example:

    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()
    }
    

    testTemplates

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

    -> Template from PSCustomObject is updated (referenced property -List1)

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

    -> Template from Class is safe

    0 讨论(0)
  • 2020-12-09 03:20

    Based on the answer by @TeraFlux, here's a function that will do a deep copy on multiple objects and accepts pipeline input.

    Note, it leverages json conversion with a default depth of 100, which lends it to a few weaknesses

    • It's going to be slow on deep or complex objects, or objects with expensive (slow) pseudoproperties (methods pretending to be properties that are calculated on the fly when asked for)
      • Though it should still be faster than the Add-Member approach because the heavy lifting is going through a compiled function
    • Anything that can't be stored in JSON may get corrupted or left behind (methods will be a prime candidate for this type of error)
      • Though any object that can safely go through this process should be savable, able to be safely stored (for recovery) or exported for transportation

    I would be interested in any caveats or improvements to deal with these

    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
        }
    

    Here are some very basic unit tests

    $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
    
    0 讨论(0)
  • 2020-12-09 03:21

    For some reason PSObject.Copy() doesn't work for all object types. Another solution to create a copy of an object is to convert it to/from Json then save it in a new variable:

    $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
    
    0 讨论(0)
提交回复
热议问题