I am teaching myself PowerShell by writing a simple parser. I use the .Net framework class Collections.Stack
. I want to modify the object at the top of the stack in place.
I know I can pop()
the object off, modify it, and then push()
it back on, but that strikes me as inelegant.
First, I tried this:
$stk = new-object Collections.Stack
$stk.push( (,'My first value') )
( $stk.peek() ) += ,'| My second value'
Which threw an error:
Assignment failed because [System.Collections.Stack] doesn't contain a settable property 'peek()'.
At C:\Development\StackOverflow\PowerShell-Stacks\test.ps1:3 char:12
+ ( $stk.peek <<<< () ) += ,'| My second value'
+ CategoryInfo : InvalidOperation: (peek:String) [], RuntimeException
+ FullyQualifiedErrorId : ParameterizedPropertyAssignmentFailed
Next I tried this:
$ary = $stk.peek()
$ary += ,'| My second value'
write-host "Array is: $ary"
write-host "Stack top is: $($stk.peek())"
Which prevented the error but still didn't do the right thing:
Array is: My first value | My second value
Stack top is: My first value
Clearly, what is getting assigned to $ary is a copy of the object at the top of the stack, so when I the object in $ary, the object at the top of the stack remains unchanged.
Finally, I read up on teh [ref] type, and tried this:
$ary_ref = [re开发者_StackOverflowf]$stk.peek()
$ary_ref.value += ,'| My second value'
write-host "Referenced array is: $($ary_ref.value)"
write-host "Stack top is still: $($stk.peek())"
But still no dice:
Referenced array is: My first value | My second value
Stack top is still: My first value
I assume the peek()
method returns a reference to the actual object, not the clone. If so, then the reference appears to be being replaced by a clone by PowerShell's expression processing logic.
Can somebody tell me if there is a way to do what I want to do? Or do I have to revert to pop()
/ modify / push()
?
An array always has a fixed size. When you're adding an item to an array, a new array with an increased length is created and the old array is copied into the new one. The reference has implicitly changed so you've got a whole new array with 2 elements whereas the stack still contains the old one. Use a List instead.
I figured it out. It was the "+=" operator that was creating the copy. It seems that you can't add elements to a .Net array. If I use a different type of object, like a hashtable, I have no trouble adding elements in place:
$stk.push( @{"1"="My first value"} )
$stk.peek()["2"]="| My second value"
write-host "Stack top keys: $($stk.peek().keys)"
write-host "Stack top values: $($stk.peek().values)"
Which yields
Stack top keys: 1 2
Stack top values: My first value | My second value
Or for a more array-like object, Collections.ArrayList works
$item = new-object Collections.ArrayList
$stk.push( $item )
$stk.peek().Add( "My first value" )
$stk.peek().Add( "| My second value" )
$obj = $stk.peek()
$obj.Add( "| My third value" )
write-host "Stack top is: $($stk.peek())"
Which outputs
Stack top is: My first value | My second value | My third value
精彩评论