ABOUT_PSCUSTOMOBJECT Short description Explains the differences between the [psobject] and [pscustomobject] type accelerators. Long description The [pscustomobject] type accelerator was added in PowerShell 3.0. Prior to adding this type accelerator, creating an object with member properties and values was more complicated. Originally, you had to use New-Object to create the object and Add-Member to add properties. For example: PS> $object1 = New-Object -TypeName PSObject PS> Add-Member -InputObject $object1 -MemberType NoteProperty -Name one -Value 1 PS> Add-Member -InputObject $object1 -MemberType NoteProperty -Name two -Value 2 PS> $object1 | Get-Member TypeName: System.Management.Automation.PSCustomObject Name MemberType Definition ---- ---------- ---------- Equals Method bool Equals(System.Object obj) GetHashCode Method int GetHashCode() GetType Method type GetType() ToString Method string ToString() one NoteProperty int one=1 two NoteProperty int two=2 PS> $object1 one two --- --- 1 2 Later, you could use the PROPERTY parameter of New-Object to pass a HASHTABLE containing the members and values. For example: PS> $object2 = New-Object -TypeName PSObject -Property @{one=1; two=2} PS> $object2 | Get-Member TypeName: System.Management.Automation.PSCustomObject Name MemberType Definition ---- ---------- ---------- Equals Method bool Equals(System.Object obj) GetHashCode Method int GetHashCode() GetType Method type GetType() ToString Method string ToString() one NoteProperty int one=1 two NoteProperty int two=2 PS> $object2 one two --- --- 1 2 Since PowerShell 3.0, casting a HASHTABLE to [pscustomobject] achieves the same result. PS> $object3 = [pscustomobject]@{one=1; two=2} PS> $object3 | Get-Member TypeName: System.Management.Automation.PSCustomObject Name MemberType Definition ---- ---------- ---------- Equals Method bool Equals(System.Object obj) GetHashCode Method int GetHashCode() GetType Method type GetType() ToString Method string ToString() one NoteProperty int one=1 two NoteProperty int two=2 PS> $object3 one two --- --- 1 2 PSOBJECT type objects maintain the list of members in the order that the members were added to the object. Even though HASHTABLE objects don't guarantee the order of the key-value pairs, casting a literal hashtable to [pscustomobject] maintains the order. The hashtable must be a literal. If you wrap the hashtable in parentheses or if you cast a variable containing a hashtable, there is no guarantee that the order is preserved. $hash = @{ Name = "Server30" System = "Server Core" PSVersion = "4.0" } $Asset = [pscustomobject]$hash $Asset PSVersion Name System --------- ---- ------ 4.0 Server30 Server Core Understanding the type accelerators [psobject] and [pscustomobject] are type accelerators. For more information, see about_Type_Accelerators. Even though you might think that [pscustomobject] should map to SYSTEM.MANAGEMENT.AUTOMATION.PSCUSTOMOBJECT, the types are different. PS> [pscustomobject] -eq [System.Management.Automation.PSCustomObject] False Both type accelerators are mapped to the same class, PSOBJECT: PS> [pscustomobject] IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True PSObject System.Object PS> [psobject] IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True PSObject System.Object When the [pscustomobject] type accelerator was added to PowerShell, it included extra code to handle conversion of a HASHTABLE to a PSOBJECT type. This extra code is only invoked when a new object is being created. Therefore, you can't use [pscustomobject] for type coercion or type comparison, because all objects are treated as PSOBJECT types. For example, using the -is operator to check that an object returned by a cmdlet is a [pscustomobject] is the same as comparing it to [psobject]. PS> (Get-Item /) -is [pscustomobject] True PS> (Get-Item /) -is [psobject] True When you cast any object to [psobject] you get the type of the original object. Therefore, casting anything other than a HASHTABLE to [pscustomobject] results in the same type. PS> ([psobject]@{Property = 'Value'}).GetType().FullName System.Collections.Hashtable PS> ([pscustomobject]123).GetType().Name Int32 PS> ([pscustomobject]@{Property = 'Value'}).GetType().FullName System.Management.Automation.PSCustomObject While, casting an object to [psobject] appears to have no affect on the type, PowerShell adds an _invisible_ [psobject] wrapper around the object. This can have subtle side effects. - Wrapped objects will match their original type and the [psobject] type. PS> 1 -is [Int32] True PS> 1 -is [psobject] False PS> ([psobject] 1) -is [Int32] True PS> ([psobject] 1) -is [psobject] True - The format operator (-f) doesn't recognized an array wrapped by [psobject]. PS> '{0} {1}' -f (1, 2) 1 2 PS> '{0} {1}' -f ([psobject] (1, 2)) Error formatting a string: Index (zero based) must be greater than or equal to zero and less than the size of the argument list.. At line:1 char:1 + '{0} {1}' -f ([psobject] (1, 2)) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: ({0} {1}:String) [], RuntimeException + FullyQualifiedErrorId : FormatError Notes In Windows PowerShell, objects created by casting a HASHTABLE to [pscustomobject] don't have the LENGTH or COUNT properties. Attempting to access these members returns $null. For example: PS> $object = [PSCustomObject]@{key = 'value'} PS> $object key --- value PS> $object.Count PS> $object.Length Starting in PowerShell 6, objects created by casting a HASHTABLE to [pscustomobject] always have a value of 1 for the LENGTH and COUNT properties. See also - about_Object_Creation - about_Objects - System.Management.Automation.PSObject - System.Management.Automation.PSCustomObject