tags:

views:

70

answers:

2

Why are the variable assignments that I make inside the Trap block not visible outside it?

$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.

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.

+1  A: 

Rather than re-write Jakul's excelent post on the subject, I'll just link it: http://huddledmasses.org/trap-exception-in-powershell/

Gobs of information on how powershell deals with error handling with plenty of detail.

slipsec
+3  A: 

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';

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.

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.

Scott Munro
Yes, your suspicion about the String variable is correct. Also, it is more accurate to say that the Trap scope inherits a copy of all variables. So it reads and writes against its own copies.
JasonMArcher
Thanks Jason. That is what I was missing. +1
Scott Munro