Surprisingly, [pscustomobject]
is the same as [psobject]
: both these type accelerators point to type [System.Management.Automation.PSObject]
, even though there _is_ a distinct [System.Management.Automation.PSCustomObject]
type.
Mostly, this conflation goes unnoticed (and has come up before - see #2295), but:
what is the rationale for it?
it makes for surprising behavior on occasion - see below.
# Even though their names strongly suggest identity, they are different types.
> [pscustomobject] -eq [System.Management.Automation.PSCustomObject]
False
# Any object returned by a *command* (as opposed to an expression)
# returns $True for -is [psobject], and therefore also [pscustomobject]
> (Get-Item /) -is [pscustomobject]
True
# Casting anything other than a hashtable literal to [psobject] is a no-op,
# and therefore also with [pscustomobject]
> ([pscustomobject] 666).GetType().Name
Int32
PowerShell Core v6.0.0-beta.4
I'm not aware of a good reason other than legacy reasons and concerns over breaking existing scripts.
Thanks, @lzybkr.
On a meta note:
While I will certainly add that information to my personal notes, my concern is that these nuggets of information - sprinkled throughout the issues reported (#4347 is another recent example) - are not documented in an easily discoverable fashion.
I've seen the Documentation Needed
label, but it is (a) used sparingly and (b) is perhaps too broad, given that the information in question is often too esoteric / advanced for the standard help topics.
I do wish we kept track of such issues, however, perhaps with something like an Advanced-Topic Documentation Needed
label.
For now, I suggest using Documentation Needed
as a way to review issues later to determine where we need to add documentation (and not just for PRs where a doc change is also needed).
Thanks for your continued depth of research into these advanced topics.
My pleasure, @SteveL-MSFT; I'm glad that you think so.
Can I suggest a few more candidates for the Documentation Needed
label? (These are just the ones I was personally involved in discussing; in some cases I hope that the behavior will be changed instead.)
@mklement0 I marked the Issues. Could you please add there that we need document or open Issues in Doc repo?
@iSazonov:
Thank you.
add there that we need document
Do you mean I should also add a _comment_ stating that documentation is needed? What would that gain us beyond the label?
I think I'll hold off on creating documentation issues until it's clearer in what format and to what extent these advanced issues will be documented.
Do you mean I should also add a comment stating that documentation is needed?
Yes, we'll keep the time of who's going to document if it's not you.
Yes, we'll keep the time of who's going to document if it's not you.
I don't know what you mean by that.
Sorry, I meant that we need clear conclusion so that anyone can document it.
@iSazonov: I see, good point. I'll see what I can do, but I hope that if I don't get around to it, others will still be able to glean from the existing comments what is documentation-worthy.
A concrete example of where the identity of [pscustomobject]
and [psobject]
is problematic: the -as
type operator - see https://github.com/PowerShell/PowerShell/issues/4343#issuecomment-348724275
Personally, I've always found this behavior extremely confusing:
PS> (New-Object -TypeName System.Management.Automation.PSObject -Property @{Property = 'Value'}).GetType().FullName
System.Management.Automation.PSCustomObject
PS> (New-Object -TypeName System.Management.Automation.PSCustomObject -Property @{Property = 'Value'}).GetType().FullName
New-Object : A constructor was not found. Cannot find an appropriate constructor for type System.Management.Automation.PSCustomObject.
PS> ([PSCustomObject]@{Property = 'Value'}).GetType().FullName
System.Management.Automation.PSCustomObject
PS> ([PSObject]@{Property = 'Value'}).GetType().FullName
System.Collections.Hashtable
@AikenBM:
Agreed - that's a nice demonstration of the issues.
The sense I'm getting is that [System.Automation.PSObject]
, as an _implementation detail_, should be _hidden_ - see #5551; hiding it completely - i.e., taking away the availability of [psobject]
as a type available to the user, is unfortunately no longer an option for reasons of backward compatibility.
Note that a [psobject]
cast to _anything_ is _seemingly_ quietly ignored, but in reality you're creating a benign-for-the-most-part-but-not-always _extra_, _hidden_ [psobject]
wrapper (see #5579):
> @{ Property = 'Value' } -is [psobject]
False
> ([psobject] @{ Property = 'Value'} ) -is [psobject]
True # !! Extra [psobject] wrapper was created.
On a related note, #4343 shows that "know thyself" doesn't apply to custom objects with respect to the -as
operator:
# Sample custom object.
> $co = [pscustomobject] @{ one = 1 }
> $co -is [pscustomobject]
True # OK
# Same with `$co -is [psobject]` due to the identity of [psobject] and [pscustombject]
> $co -is [System.Management.Automation.PSCustomObject]
True # OK
# Now try -as
> $co -as [System.Management.Automation.PSCustomObject]
# !! $null - $co doesn't know its own type
Most helpful comment
Personally, I've always found this behavior extremely confusing: