Search code examples
powershellproperties

Is it not possible to add a property to a PsCustomObject with 'psobject.Properties.Add()'?


Reading Kevin Marquette's excellent article again, Powershell: Everything you wanted to know about PSCustomObject, he shows that its possible to remove a property from a [PsCustomObject] with the following:

$SomeObject.psobject.properties.remove('SomeProperty')

I really like this as it is consistent with working with hashtables ($hash.Remove()) and means I can avoid piping things to Select-Object or Where-Object.

Now I just need a Add() method, looking around I discovered that there is a Add() method in $SomeObject.psobject.Properties:

void Add(System.Management.Automation.PSPropertyInfo member)
void Add(System.Management.Automation.PSPropertyInfo member, bool preValidated)

But trying to use it gives me an error:

$o.psobject.Properties.Add("bar", "bar")

# MethodException: Cannot find an overload for "Add" and the argument count: "2".

Am I doing this the wrong way??


Solution

  • You're going the right path but as you can see in the method's overloads, both take a PSPropertyInfo as their first argument, and this type is an abstract type so you would need to use any type inheriting this base class instead, if you want to add a new property to your object for example, you would be using PSNoteProperty:

    $someobject = [psobject]::new()
    $someobject.PSObject.Properties.Add([psnoteproperty]::new('foo', 'bar'))
    $someobject
    
    # foo
    # ---
    # bar
    

    This is essentially how Add-Member works behind the scenes when you're looking to add a new property to the psobject wrapper.

    If you want to find out all types that could be inheriting PSPropertyInfo you can use the ClassExplorer Module:

    Find-Type -InheritsType System.Management.Automation.PSPropertyInfo
    
    #    Namespace: System.Management.Automation
    # 
    # Access        Modifiers           Name
    # ------        ---------           ----
    # public        class               PSAliasProperty : PSPropertyInfo
    # public        class               PSCodeProperty : PSPropertyInfo
    # public        class               PSProperty : PSPropertyInfo
    # public        class               PSAdaptedProperty : PSProperty
    # public        class               PSNoteProperty : PSPropertyInfo
    # public        class               PSVariableProperty : PSNoteProperty
    # public        class               PSScriptProperty : PSPropertyInfo
    

    Or you can filter those types in your AppDomain where their .BaseType contains PSPropertyInfo, do note this query would return less results because it only filters 1 level up in the base type hierarchy:

    [System.AppDomain]::CurrentDomain.GetAssemblies().GetTypes() |
        Where-Object BaseType -EQ ([System.Management.Automation.PSPropertyInfo]) |
        Where-Object IsPublic
    
    #    Namespace: System.Management.Automation
    # 
    # Access        Modifiers           Name
    # ------        ---------           ----
    # public        class               PSAliasProperty : PSPropertyInfo
    # public        class               PSCodeProperty : PSPropertyInfo
    # public        class               PSProperty : PSPropertyInfo
    # public        class               PSNoteProperty : PSPropertyInfo
    # public        class               PSScriptProperty : PSPropertyInfo