Я хочу создать новый экземпляр моего пользовательского объекта PSObject. У меня есть объект Button, созданный как PSObject, и я хочу создать новый объект Button2, который имеет те же элементы, что и Button, но я не могу найти способ клонирования исходного объекта, не ссылаясь на исходный объект (если я изменяю свойство в Button2 также изменяется в Button). Есть ли способ сделать это так же, как с hashtables и массивами с помощью некоторого метода Clone()?
Как создать новый экземпляр clone объекта PSObject
Ответ 1
В действительности нет клонного метода! Однако там, где есть желание...
$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
Ответ 2
Самый простой способ - использовать Copy Метод PsObject
== > $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
Ответ 3
Другая возможность:
$o1 = New-Object PsObject -Property @{ prop1='a' ; prop2='b' }
$o2 = $o1 | select *
$o2.prop1 = 'newvalue'
$o1.prop1
$o2.prop1
a
newvalue
Ответ 4
По какой-то причине PSObject.Copy() не работает для всех типов объектов. Другим решением для создания копии объекта является преобразование его в/из 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
Ответ 5
Вот пример [pscustomobject] со скрытым .psobject.copy():
$a = [pscustomobject]@{message='hi'}
$a.message
hi
$b = $a.psobject.copy()
$b.message
hi
$a.message = 'there'
$a.message
there
$b.message
hi
Ответ 6
Поместите это в класс Utility или определите в текущем разделе
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
Ответ 7
Начиная с 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()
}
testTemplates
PS C:\Windows\system32> testTemplates
List1 String1 Bool1
----- ------- -----
{Value} value1 False
-> Обновлен шаблон из PSCustomObject (ссылка на свойство -List1)
List1 String1 Bool1
----- ------- -----
{} value1 False
-> Шаблон из класса безопасен
Ответ 8
Основываясь на ответе @TeraFlux, здесь функция, которая делает глубокое копирование нескольких объектов и принимает конвейерный ввод.
Обратите внимание, что он использует преобразование JSON с глубиной по умолчанию 100, что делает его несколько слабым
- Он будет медленным на глубоких или сложных объектах или объектах с дорогими (медленными) псевдосвойствами (методы, представляющие собой свойства, которые вычисляются на лету по запросу)
- Хотя это все равно должно быть быстрее, чем подход 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