Workaround for Add-Member on plain .Net objects

PowerShell Team


While I was fixing bugs, I came across an interesting bug ( that seemed to have a very simple fix upon first glance. However, after doing some more research, I found out that it was actually very tricky to fix the root cause. If you do the following:

$table = @{ key1="val1"; key2="val2" }
Add-Member -in $table -MemberType NoteProperty -Name test -Value testValue

Now if you pipe $table to Get-Member you will notice that the test property is missing. No matter how many times you repeat the same Add-Member operation, $table seems to be totally unaffected. However, if you access the test property or even a nonexistent property before you use Add-Member, the command will behave correctly.

Why is this happening?

In PowerShell, we create .Net objects as System.Object as opposed to System.Management.Automation.PSObject for performance reasons. This is because every time an object is converted from System.Object to PSObject, PowerShell has to build the metadata information by reflecting on the .Net object. Doing so can introduce unnecessary overhead since not all operations require PSObjects. When you do something with it (i.e. property access), it becomes a PSObject. However, when you pass a variable ($table) bound to an Object to a cmdlet such as Add-Member, the cmdlet internally converts the bound object into a PSObject. This effectively creates a copy of the original object and anything done to the PSObject is not reflected on the input object.

How can we work around it?

We are hoping to fix the root cause in the next release. However, the workaround is actually quite simple. Since Add-Member is able to emit the modified object if the –PassThru flag is specified, all you have to do is assigned the object back to your initial variable.

$table = Add-Member -in $table NoteProperty Test "Test property" –PassThru

Tianjie (James) Wei [MSFT]
Software Design Engineer
Windows PowerShell Team


Discussion is closed.

Feedback usabilla icon