Search notes:

PowerShell: the PSObject object

Under the hood, all PowerShell objects are PSObject objects

All objects that are created in a PowerShell session, be they .NET types, COM objects or the explicit «void» object PSCustomObject, are wrapped into a PSObject, which itself is implemented in the .NET type System.Management.Automation.PSObject.
These PSObjects might be stored in a variable (to name them) or flow (anonymously) through a pipeline.
In order to demonstrate of wrapping objects into a PSObject, I create a few objects …
$forty_two  = 42
$now        = new-object  System.DateTime
$com_obj    = new-object -comObject shell.application
$a_process  =(get-process)[0]
$void_obj   = new-object  psCustomObject
… and assign them to an array so that I can iterate over each element of them:
$all_objects = $forty_two, $now, $com_obj, $a_process, $void_obj
First, I have the object's types printed. I also print the type of the an object's special member psObject which directly accesses the psObject object that wraps the underlying object:
$all_objects | foreach-object {
   "$($_.psObject.GetType().FullName):  $($_.GetType().FullName)"
}
Which prints
System.Management.Automation.PSObject:  System.Int32
System.Management.Automation.PSObject:  System.DateTime
System.Management.Automation.PSObject:  System.__ComObject
System.Management.Automation.PSObject:  System.Diagnostics.Process
System.Management.Automation.PSObject:  System.Management.Automation.PSCustomObject
This dichthomy of an object being both, a PSObject object and a .NET (or COM) type that is pointed at by the PSObject, allows for some interesting features in PowerShell, for example adding members to an object which is demonstrated below.

Adding (extended) members to a PowerShell object

I use new-object to create an instance of a .NET System.Int64 class:
$obj = new-object System.Int64 
Github repository about-PowerShell, path: /language/object/psObject/create-object.ps1
With add-member, it's possible to add user defined members to $obj:
$obj | add-member -memberType noteProperty -name theNumber -value  42
$obj | add-member -memberType noteProperty -name someText  -value 'foo, bar, baz'

write-output "num = $($obj.theNumber), text = $($obj.someText)"
#
# num = 42, text = foo, bar, baz
Github repository about-PowerShell, path: /language/object/psObject/add-member.ps1
How is it possible to add members to a .NET class?
Answer: members can be added because of the Extended Type System (ETS): the members are not added to the .NET object but to the PSObject that wraps the .NET object.
Extended members that were added to a object can be displayed with get-member -view extended:
$obj | get-member -view extended
#
#     TypeName: System.Int64
#  
#  Name      MemberType   Definition
#  ----      ----------   ----------
#  someText  NoteProperty string someText=foo, bar, baz
#  theNumber NoteProperty int theNumber=42
Github repository about-PowerShell, path: /language/object/psObject/obj-get-extended-members.ps1
Technically, the .NET class is wrapped into a System.Management.Automation.PSObject object. This PSObject object is referenced by $obj.psObject:
$psObj = $obj.psObject

$psObj.GetType().FullName
#
# System.Management.Automation.PSObject
Github repository about-PowerShell, path: /language/object/psObject/psObject.ps1
The PSObject object has a member Properties which stores the extended values:
$psObj.properties
#
#  MemberType      : NoteProperty
#  IsSettable      : True
#  IsGettable      : True
#  Value           : 42
#  TypeNameOfValue : System.Int32
#  Name            : theNumber
#  IsInstance      : True
#  
#  MemberType      : NoteProperty
#  IsSettable      : True
#  IsGettable      : True
#  Value           : foo, bar, baz
#  TypeNameOfValue : System.String
#  Name            : someText
#  IsInstance      : True
Github repository about-PowerShell, path: /language/object/psObject/psObject-properties.ps1
Finally, there is also the BaseObject member which refers to the System.Int64 that we created at the beginning:
$psObj.baseObject.GetType().FullName
#
# System.Int64
Github repository about-PowerShell, path: /language/object/psObject/psObject-baseObject.ps1

Adding a ToString() method

The following example adds a ToString() method to a PsObject instance. Because all objects already have a ToString() method, add-member cmdLet requires the -force option.
$psObj_1 = @{ val_1 = 'one'   ; val_2 = 'two'   ; val_3 = 'three' }
$psObj_2 = @{ val_1 = 'foo'   ; val_2 = 'bar'   ; val_3 = 'baz'   }

$psObj_2 | add-member               `
   -memberType scriptMethod         `
   -name       ToString {
          "val 1: $($this.val_1), " +
          "val 2: $($this.val_2), " +
          "val 3: $($this.val_3)"
   }                                `
   -force

$psObj_1.ToString()
#
#  System.Collections.Hashtable

$psObj_2.ToString()
#
#  val 1: foo, val 2: bar, val 3: baz


#
#  Unfortunately, this version of ToString is
#  not used when not called explicitetly, as
#  for example when strings are interpolated:
#
write-host "psObj_1: $psObj_1"
#
#  psObj_1: System.Collections.Hashtable

write-host "psObj_2: $psObj_2"
#
#  psObj_2: System.Collections.Hashtable
Github repository about-PowerShell, path: /language/object/psObject/ToString.ps1

See also

System.Management.Automation.PSCustomObject

Index