Question:
Why are the variable assignments that I make inside the Trap block not visible outside it?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
$integer = 0; $string = [String]::Empty; $stringBuilder = new-object 'System.Text.StringBuilder'; trap { $integer = 1; $string = '1'; $stringBuilder.Append('1'); write-host "Integer Variable Inside: " $integer; write-host "String Variable Inside: " $string; write-host "StringBuilder Variable Inside: " $stringBuilder; continue; } $dummy = 1/$zero; write-host "Integer Variable Outside: " $integer; write-host "String Variable Outside: " $string; write-host "StringBuilder Variable Outside: " $stringBuilder; |
I would have expected the results from within and outside the Trap block to be identical but these are the results that I am seeing.
1 2 3 4 5 6 7 |
Integer Variable Inside: 1 String Variable Inside: 1 StringBuilder Variable Inside: 1 Integer Variable Outside: 0 String Variable Outside: StringBuilder Variable Outside: 1 |
Notice that it is only the StringBuilder that retains its value.
I am guessing that this has something to do with the difference between value and reference types but can’t quite pin it down.
Answer:
With info that slipsec provided above and through some further experimentation, I now understand what is happening here.
Joel explains how the Trap scope works as follows.
Even though in our error handler we
were able to access the value of
$Result and see that it was True … and
even though we set it to $False, and
printed it out so you could see it was
set … the function still returns True,
because the trap scope doesn’t modify
the external scope unless you
explicitly set the scope of a
variable. NOTE: If you had used
$script:result instead of $result (in
every instance where $result appears
in that script), you would get the
output which the string/comments led
you to expect.
So variables from outside the Trap scope can be read but not set because they are copies of the originals (thanks Jason). This is the reason why the Integer variable did not retain its value. The StringBuilder however, is a reference object and the variable is only a pointer to that object. The code within the Trap scope was able to read the reference that the variable was set to and modify the object to which it was pointing – the variable itself required no change.
Note that Joel’s tip about specifying the scope of the variable allowed me to set the value of the Integer variable from within the Trap scope.
$script:integer = 0;
$string = [String]::Empty;
$stringBuilder = new-object ‘System.Text.StringBuilder’;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
trap { $script:integer = 1; $string = '1'; $stringBuilder.Append('1'); write-host "Integer Variable Inside: " $script:integer; write-host "String Variable Inside: " $string; write-host "StringBuilder Variable Inside: " $stringBuilder; continue; } $dummy = 1/$zero; write-host "Integer Variable Outside: " $script:integer; write-host "String Variable Outside: " $string; write-host "StringBuilder Variable Outside: " $stringBuilder; |
…and these are the results.
1 2 3 4 5 6 7 |
Integer Variable Inside: 1 String Variable Inside: 1 StringBuilder Variable Inside: 1 Integer Variable Outside: 1 String Variable Outside: StringBuilder Variable Outside: 1 |
Note that the string variable does not retain its value because although it is a reference type, it is also immutable.