admin管理员组文章数量:1225020
I saw some code like below:
$ht1 = @{
age=13
course="Children Fund"
}
$var1 = [PSObject]$ht1
$json = $var1 | ConvertTo-Json
From what I know, [PSObject] is a type casting, and the $var1.GetType() will still return "HashTable", but I think such type casting are wrapping the ht1 as the PSObject documentation said, is this reasoning right ? It looks after it wrapped the ht1 but the type of $var1 will be still HashTable, I think this is the magic of PowerShell implementation, am I right ?
If that is just a wrapping benefit it brings when doing explicit type casting
[PSObject]$ht1
I wonder is there some other benefits it brings when doing such explicit type casting ? And is such type casting really needed when passing the $var1 to the downstream pipeline part ConvertTo-Json cmdlet ?
I saw some code like below:
$ht1 = @{
age=13
course="Children Fund"
}
$var1 = [PSObject]$ht1
$json = $var1 | ConvertTo-Json
From what I know, [PSObject] is a type casting, and the $var1.GetType() will still return "HashTable", but I think such type casting are wrapping the ht1 as the PSObject documentation said, is this reasoning right ? It looks after it wrapped the ht1 but the type of $var1 will be still HashTable, I think this is the magic of PowerShell implementation, am I right ?
If that is just a wrapping benefit it brings when doing explicit type casting
[PSObject]$ht1
I wonder is there some other benefits it brings when doing such explicit type casting ? And is such type casting really needed when passing the $var1 to the downstream pipeline part ConvertTo-Json cmdlet ?
Share Improve this question asked Feb 5 at 21:00 IcyBrkIcyBrk 1,2701 gold badge13 silver badges22 bronze badges 1 |2 Answers
Reset to default 3Why Use [PSObject] Type Casting
Generally, do NOT use [psobject]
casts: In most cases, it is at best an unnecessary, virtual no-op and at worst results in unexpected behavior.
[psobject]
is a meant-to-be-invisible helper type used behind the scenes to transparently wrap instances of (other) .NET types.- It is for this reason that a
[psobject]
instance that wraps an instance of a .NET type acts just like the latter, as you've observed, and it is only a-is [psobject]
test that reveals whether you're dealing with a[psobject]
-wrapped object or not.
- It is for this reason that a
Thus, a
[psobject]
cast is - in most cases - a no-op.[1]Unfortunately, however, the implementation of this conceptually transparent wrapper is leaky, resulting in situationally different behavior if such a wrapper is present: for details and a list of such situations, see GitHub issue #5579.
However, implicitly created
[psobject]
wrappers cannot be avoided:- Notably, all objects output to the success stream by binary cmdlets are
[psobject]
-wrapped (which implies that objects created withNew-Object
are wrapped, unlike ones created with the intrinsic, static::new()
method). Also, objects received via the pipeline that bind to an untyped aka[object]
-typed parameter are wrapped, whether or not they are received by a binary cmdlet or a PowerShell function or script block; additionally, many built-in cmdlets explicitly type their "untyped" parameters as[psobject]
.
See this answer for details.
- Notably, all objects output to the success stream by binary cmdlets are
What DOES make sense is to cast a hashtable or its ordered variant ([ordered]
) to [pscustomobject]
rather than [psobject]
, in order to construct a [pscustomobject]
instance, PowerShell's "property bag" type (i.e., an object with dynamically defined properties):
Note that, technically,
[psobject]
and[pscustomobject]
refer to the same type,System.Management.Automation.PSObject
, but only[pscustomobject]
(meaningfully) supports casting from (ordered) hashtables, most typically in the form of a literal definition, where a hashtable literal is (seemingly) cast to[pscustomobject]
, e.g.
[pscustomobject] @{ Foo = 1; Bar = 2 }
.
Note that this is syntactic sugar that results in direct construction of a[pscustomobject]
instance, with the entry-definition order preserved (constructing a hashtable in isolation would not preserve the definition order, as is the case if you cast from a hashtable created beforehand, in a separate step).Pitfalls:
Casting instance of types other than (ordered) hashtables to
[pscustomobject]
unfortunately behaves like casting to[psobject]
, and is therefore almost always pointless, for the reasons explained above. GitHub issue #20756 suggests preventing such counterintuitive, pointless casts.It would make sense to more generally support casting instances of all
System.Collections.IDictionary
-implementing types to[pscustomobject]
, not just (ordered) hashtables, which is currently not the case; GitHub issue #20753 is an feature request to that effect.
In PowerShell terms, a
[pscustomobject]
"property bag" is a[psobject]
instance that (conceptually) has no base object,[2] i.e. it doesn't wrap another object; rather it is composed solely of ETS (Extended Type System) members.
[1] Since PowerShell v3, [psobject]
wrappers are actually no longer necessary for decorating .NET instances with ETS (Extended Type System) members due to use of resurrection tables, with two exceptions: [string]
instances and .NET value type instances (such as numbers), for which the resurrection tables cannot be used. Because of the ephemeral nature of such wrappers, decorating instances of these types is generally best avoided. If you want to decorate them nonetheless, use Add-Member
with the -PassThru
switch and save the resulting [psobject]
instance, to which the new member is directly attached (rather than via the resurrection tables); e.g., $decoratedString = 'Decorate me' | Add-Member -PassThru Foo 42; $decoratedString.Foo
[2] Technically, a placeholder is used as the base object, which is a singleton of the otherwise unused System.Management.Automation.PSCustomObject
type. Confusingly, is not the same as [pscustomobject]
. Because a property-bag [psobject]
== [pscustomobject]
still technically behaves like a transparent wrapper, calling .GetType()
on it therefore reports System.Management.Automation.PSCustomObject
as its type, and to detect such an instance you must use -is [System.Management.Automation.PScustomObject]
, not -is [pscustomobject]
- see GitHub issue #11921 for a discussion of this counterintuitive behavior.
Also, even when spelling out [System.Management.Automation.PScustomObject]
there's an outright bug with respect to the -as
operator - see GitHub issue #4343
I wonder is there some other benefits it brings when doing such explicit type casting ?
None that I can think of other than the string
example shown below.
And is such type casting really needed when passing the
$var1
to the downstream pipeline partConvertTo-Json
cmdlet ?
For the question being asked, you really don't need to wrap your hashtable for ConvertTo-Json
to work correctly.
You're correct in that PSObject
serves the purpose of an invisible wrapper. The .NET doc already describes it perfectly:
Wraps an object providing alternate views of the available members and ways to extend them. Members can be methods, properties, parameterized properties, etc.
Those members referred to in the description that could be added to a PSObject
can be:
[psobject].Assembly.GetTypes() |
Where-Object { $_.IsSubclassOf([System.Management.Automation.PSMemberInfo]) }
As for alternative views they're probably referring to adding the special PSTypeName
property and then assigning a formatting or extended type data, see for example: How can I set left/right column justification in Powershell's "format-table".
They could also refer to adding a PSMemberSet
:
$object = [pscustomobject]@{
foo = 1
bar = 2
baz = 3
}
$propertySet = [System.Management.Automation.PSPropertySet]::new(
'DefaultDisplayPropertySet', [string[]] ('foo', 'bar'))
$object.PSObject.Members.Add(
[System.Management.Automation.PSMemberSet]::new(
'PSStandardMembers',
[System.Management.Automation.PSPropertySet[]] $propertySet))
# $object now hides `baz` property in the default display
$object
There is usually no need to wrap an object in PSObject
manually, however you can do so if you want to extend that object. For example for a string, if you wrap it you could add a new property to it:
$string = [psobject] "a string"
$string.PSObject.Properties.Add([psnoteproperty]::new('NewProperty', 'hi'))
$string # still shows the actual value
$string.NewProperty # but now has a hidden property
PSObject
is also the base for creating PSCustomObject
:
[pscustomobject]@{ NewProperty = 'hi' }
Which is essentially a PSObject
with PSNoteProperty
added to it:
$acustomObject = [psobject]::new()
$acustomObject.PSObject.Properties.Add([psnoteproperty]::new('NewProperty', 'hi'))
$acustomObject
本文标签: powershellWhy Use PSObject Type CastingStack Overflow
版权声明:本文标题:powershell - Why Use [PSObject] Type Casting - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1739440483a2163220.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
ConvertTo-Json
would work just fine whiteout the need to wrapping it in aPSObject
. Unsure where you got that it was required – Santiago Squarzon Commented Feb 5 at 21:07