scobi
scobi

Reputation: 14548

Why does this bizarre behavior with add-member on built-in types in Powershell happen?

Given this:

$x = new-object psobject
$x | add-member noteproperty test 'xtest'
$x.test
$x | add-member noteproperty test2 'xtest2'
$x.test2

The output is what I'd expect:

xtest
xtest2

But given this:

$y = @{}
$y | add-member noteproperty test 'ytest'
$y.test
$y | add-member noteproperty test2 'ytest2'
$y.test2

I simply get:

ytest2

I'm confused. And if I do this:

$y = @{}
$y | add-member noteproperty test 'ytest'
$y | add-member noteproperty test2 'ytest2'
$y.test
$y.test2

Then there is no output at all. Running through get-members confirms that the methods are not actually being added.

What's going on here? This has to be something dumb on my end, but I can't see it.

Upvotes: 4

Views: 284

Answers (2)

Keith Hill
Keith Hill

Reputation: 201652

IIRC this really has to do with PSObject wrappers which is a key piece of the extended type system in PowerShell 2.0. When you execute this:

$x = new-object psobject
$x | add-member noteproperty test 'xtest'
$x.test

It works because the object is already a PSOject so the Add-Member can add the new NoteProperty directly to the PSObject e.g.:

$y = @{}
$y | add-member noteproperty test 'ytest'
$y.test

This doesn't work because $y isn't initially wrapped so when you execute Add-Member, it creates a new object that wraps the original hashtable. You can see this by using Get-Member e.g.:

$y | Get-Member

You won't see your test property. To get this to work in v2, you have to do this:

$y = $y | add-member noteproperty test ytest -passthru
$y.test
ytest

FYI, this changes in V3 since it is based on the DLR it modifies the object directly without creating a new wrapper object e.g.:

# PowerShell V3 only
16# $y = @{}
17# Add-Member -InputObject $y test ytest
18# $y.test
ytest

Upvotes: 3

rerun
rerun

Reputation: 25495

This is due to power shell unrolling your list which is empty. If you want to add a property to the list use the inputobject paramater

add-member -input $y noteproperty test1 'ytest1'

the usage you described in the question would add a member to every item in the list which you can see in this example

C:(...)SequenceId>$y = @()
C:(...)SequenceId>$y += New-Object -t psobject
C:(...)SequenceId>$y += New-Object -t psobject
C:(...)SequenceId>$Y | Add-Member -type noteproperty -name hi -value you
C:(...)SequenceId>$Y

hi
--
you
you

@Joey raised this issue that I used a array which is directly iterable. To get the OP semantic to do what I said you would have to do the folowing

C:(...)SequenceId>$y = @{}
C:(...)SequenceId>$y["one"] = New-Object -t psobject
C:(...)SequenceId>$y["two"] = New-Object -t psobject
C:(...)SequenceId>$Y | Add-Member -type noteproperty -name hi -value you
C:(...)SequenceId>$y

Name                           Value
----                           -----
two
one

C:(...)SequenceId>$Y.values | Add-Member -type noteproperty -name hi -value you
C:(...)SequenceId>$y

Name                           Value
----                           -----
two                            @{hi=you}
one                            @{hi=you}

Upvotes: 0

Related Questions