Search notes:

System.Management.Automation.PSObject (class)

Each PowerShell object is an instance of System.Management.Automation.PSObject.
Such a PSObject has the member _immediateBaseObject that contains a reference to the actual object, which can by any .NET object, that is manipulated through the PowerShell object.
A PSObject's public properties can be accessed from a PowerShell object via its psObject property: $MyPowerShellObject.psObject.
PSObject is the central class that allows the Extended Type System of PowerShell.

PSObject, its base object and PSCustomObject

In the source code, PSObject has the following interesting snippets (heavily redacted by me for brevity) that I believe shed some light on how PSObject and PSCustomObject play together.
In PSObject, the field _immediateBaseObject contains a reference to the object that a PowerShell object is storing (via PSObject).
If the constructor of PSObject is called with an object (public PSObject(object obj)), the reference of _immediateBaseObject is set to that object by calling CommonInitialization(obj).
However, if the constructor without argument (public PSObject()) is used to initialize a PSObject instance, CommonInitialization() is called with PSCustomObject.SelfInstance. This expression refers to the PSCustomObject singleton(?). Thus, _immediateBaseObject is initialized with a reference to PSCustomObject (see also definition of PSCustomObject).
In summary, PSCustomObject is used to intialize the base object of PSObject in absence of any other .NET object. This indicates that the PSObject is just used to store one or more NoteProperty (?).
public class PSObject : IFormattable, IComparable, ISerializable, IDynamicMetaObjectProvider
{
    /// This is the main field in the class representing
    /// the System.Object we are encapsulating.
    private object _immediateBaseObject;


    /// Initializes a new instance of PSObject with an PSCustomObject BaseObject.
    public PSObject()
    {
        CommonInitialization(PSCustomObject.SelfInstance);
    }

    /// Initializes a new instance of PSObject wrapping obj (accessible through BaseObject).
    public PSObject(object obj)
    {
        CommonInitialization(obj);
    }

    private void CommonInitialization(object obj)
    {
        if (obj is PSCustomObject)
        {
            this.ImmediateBaseObjectIsEmpty = true;
        }

        _immediateBaseObject = obj;
    }


    /// Gets the object we are directly wrapping.
    ///    If the ImmediateBaseObject is another PSObject,
    ///    that PSObject will be returned.
    public object ImmediateBaseObject => _immediateBaseObject;


    /// Gets the object we are wrapping.
    ///    If the ImmediateBaseObject is another PSObject, this property
    ///    will return its BaseObject.
    public object BaseObject
    {
        get
        {
            object returnValue;
            PSObject mshObj = this;
            do
            {
                returnValue = mshObj._immediateBaseObject;
                mshObj = returnValue as PSObject;
            } while (mshObj != null);

            return returnValue;
        }
    }

}

Public properties

The PSObject class exhibits six public properties:
//  Gets the member collection.
    public PSMemberInfoCollection<PSMemberInfo> Members …

//  Gets the Property collection, or the members that are actually properties.
    public PSMemberInfoCollection<PSPropertyInfo> Properties …

//  Gets the Method collection, or the members that are actually methods.
    public PSMemberInfoCollection<PSMethodInfo> Methods

//  Gets the object we are directly wrapping.
// (If the ImmediateBaseObject is another PSObject, that PSObject will be returned.)
    public object ImmediateBaseObject => _immediateBaseObject;

//  Gets the object we are wrapping
// (If the ImmediateBaseObject is another PSObject, this property will return its BaseObject.)
    public object BaseObject …

//  Gets the type names collection initially containing the object type hierarchy.
    public Collection<string> TypeNames …
These properties can be accessed via a PowerShell object's psObject member:
PS C:\> $obj = get-date
PS C:\> $obj.psObject.Members
PS C:\> $obj.psObject.Properties
PS C:\> $obj.psObject.Methods
PS C:\> $obj.psObject.ImmediateBaseObject
PS C:\> $obj.psObject.BaseObject
PS C:\> $obj.psObject.TypeNames

Difference to System.Management.Automation.PSCustomObject

Jason Shirk provided a helpful answer on StackOverflow regarding the difference between a PSObject and a PSCustomObject. I believe the gist of his answer is the following:
The System..Management.Automation.PSObject object wraps other objects. It is mostly used in PowerShell to have a consistent object for different underlying object types (.NET, WMI, COM, ADSI, property bags etc…)
If the wrapped object is a property bag, the property bag is stored in a System.Management.Automation.PSCustomObject object.

TODO: fields

The class has also the following interesting fields that should eventually be described:
private WeakReference<TypeTable> _typeTable;
private AdapterSet _adapterSet;
private PSMemberInfoInternalCollection<PSMemberInfo> _instanceMembers;
private PSMemberInfoIntegratingCollection<PSMemberInfo> _members;
private PSMemberInfoIntegratingCollection<PSPropertyInfo> _properties;
private PSMemberInfoIntegratingCollection<PSMethodInfo> _methods;

private PSObjectFlags _flags;
PSMemberInfo is the base class for all members of a PSObject.
PSPropertyInfo is the base class for all members of a PSObject that behave like a property.
PSMethodInfo is the base class for all members of a PSObject that behave like a method.

See also

PowerShell: the PSObject object
The PowerShell type accelerator for System.Management.Automation.PSObject is [pscustomobject] and [psobject].

Links

The source code of PSObject.

Index