Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Why is [pscustomobject] the same as [psobject], even though a distinct [System.Management.Automation.PSCustomObject] type exists? #4344

Closed
mklement0 opened this issue Jul 26, 2017 · 13 comments
Labels
Issue-Question ideally support can be provided via other mechanisms, but sometimes folks do open an issue to get a Resolution-Answered The question is answered. WG-Engine core PowerShell engine, interpreter, and runtime

Comments

@mklement0
Copy link
Contributor

mklement0 commented Jul 26, 2017

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

Environment data

PowerShell Core v6.0.0-beta.4
@iSazonov iSazonov added WG-Engine core PowerShell engine, interpreter, and runtime Issue-Question ideally support can be provided via other mechanisms, but sometimes folks do open an issue to get a labels Jul 26, 2017
@lzybkr
Copy link
Member

lzybkr commented Jul 26, 2017

I'm not aware of a good reason other than legacy reasons and concerns over breaking existing scripts.

@mklement0
Copy link
Contributor Author

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.

@SteveL-MSFT SteveL-MSFT added the Documentation Needed in this repo Documentation is needed in this repo label Jul 26, 2017
@SteveL-MSFT
Copy link
Member

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.

@iSazonov iSazonov added the Resolution-Answered The question is answered. label Aug 3, 2017
@iSazonov
Copy link
Collaborator

iSazonov commented Aug 3, 2017

@mklement0 I marked the Issues. Could you please add there that we need document or open Issues in Doc repo?

@mklement0
Copy link
Contributor Author

@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.

@iSazonov
Copy link
Collaborator

iSazonov commented Aug 4, 2017

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.

@mklement0
Copy link
Contributor Author

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.

@iSazonov
Copy link
Collaborator

iSazonov commented Aug 4, 2017

Sorry, I meant that we need clear conclusion so that anyone can document it.

@mklement0
Copy link
Contributor Author

@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.

@mklement0
Copy link
Contributor Author

A concrete example of where the identity of [pscustomobject] and [psobject] is problematic: the -as type operator - see #4343 (comment)

@AikenBM
Copy link

AikenBM commented Mar 7, 2018

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

@mklement0
Copy link
Contributor Author

mklement0 commented Mar 7, 2018

@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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Issue-Question ideally support can be provided via other mechanisms, but sometimes folks do open an issue to get a Resolution-Answered The question is answered. WG-Engine core PowerShell engine, interpreter, and runtime
Projects
None yet
Development

No branches or pull requests

6 participants