1

So, it is possible to clear a variable with the Clear-Variable cmdlet which is the same as giving it the value $null.

One can also remove a variable with the Remove-Variable cmdlet or the Remove-Item cmdlet for environmental variables.

My question is, is stringing variables together like this...

$Env:var1 = $var2 = $var3 = $null

... a legitimate way of clearing variables or even assigning values?

I know that $var1 is independent from $var2 if you assign values like this: $var1 = $var2 = 1, but when the value is an object created with the New-Object cmdlet, both variables point to the same object:

$var1 = $var2 = New-Object System.Diagnostics.Stopwatch
$var1.Start()
Start-Sleep 1
$var2.Stop()
Start-Sleep 1
$var1.Elapsed.TotalMilliseconds

Is this solely down the value being a Microsoft .NET Framework or COM object?

mklement0
  • 382,024
  • 64
  • 607
  • 775
FatalBulletHit
  • 762
  • 6
  • 22
  • `$var2 = $var1 = New-Object System.Diagnostics.Stopwatch;;$var1 -eq $var2` returns `true` while `$var2 = New-Object System.Diagnostics.Stopwatch; $var1 = New-Object System.Diagnostics.Stopwatch;$var1 -eq $var2` returns `false`. Don't ask me why… – JosefZ Oct 26 '19 at 11:59
  • 3
    Read [PowerShell Reference Types And Value Types](https://johnfabry.azurewebsites.net/2015/06/26/powershell-reference-types-and-value-types/) and universal [Difference between a Value Type and a Reference Type](http://net-informations.com/faq/general/valuetype-referencetype.htm). – JosefZ Oct 26 '19 at 12:50
  • @JosefZ Based on what I understand from the articles you linked (thanks!), using the `New-Object` cmdlet creates a reference type variable. – FatalBulletHit Oct 26 '19 at 14:52
  • I don't think so; try e.g.: `$a = $b = New-Object string -ArgumentList "abc";$b='ccc'; $a,$b` – JosefZ Oct 26 '19 at 15:38
  • 1
    @JosefZ: Good pointers, and true that `New-Object` is equally capable of creating value types. Your specific example needs explaining, though: as an _exception_, `[string]` (.NET strings) _act like_ value types, but are _technically_ reference types. – mklement0 Oct 26 '19 at 19:57

1 Answers1

4

... a legitimate way of clearing variables

Yes, something like $var1 = $var2 = $null (to use just 2 variables as an example) is a legitimate way of clearing variables' values, PROVIDED THAT the target variable(s) are actually defined in the local (current) scope.

If they are merely visible from a parent (ancestral) scope), you'll inadvertently create local variables of the same name whose value is $null - see this answer.

The same applies to Clear-Variable var1, var2 in principle, except that it will emit an error if no local variable(s) $var1 and $var2 are defined; additionally, Clear-Variable allows targeting a different scope explicitly with -Scope.

With limitations, you can also target other scopes with variable assignments, using scope specifiers such as $script:var - see this related answer.

Remove-Variable acts similarly to Clear-Variable and also supports -Scope, but truly removes (undefines) variables from the implied or targeted scope.

Note that accessing a removed / non-existent variable by default is effectively the same as accessing an existing variable whose value is $null. However, with Set-StrictMode -Version 1 or higher in effect, accessing a non-existent variable fails by design.

$var1 = $var2 = $null has the following advantages over Clear-Variable var1, var2:

  • It is more concise and also faster (no cmdlet call needed).
  • It can be used to implicitly create variables with a $null value, or, to put it differently, the statement doesn't fail if one of the variables doesn't exist.
  • It can also be used with environment variables (e.g., $env:FOO), though note that assigning $null (or '', the empty string) to environment variables actually removes them ($env:FOO = $null is equivalent to Remove-Item Env:FOO).

Caveat:

Assigning $null or calling Clear-Variable doesn't necessarily result in the variable containing $null:

Variables may be type-constrained, in which case they only permit storing values of the type locked in at creation (initialization) time:

[int] $i = 1  # Create a type-constrained variable that can only store [int] values.

When you assign $null to a type-constrained variable, PowerShell attempts to convert $null to the locked-in type, which can have two outcomes:

  • The conversion succeeds, in which case the converted value of the locked-in type is the new value; for instance, in the case of [int], that value is 0, because [int] $null is 0.

    [int] $i = 1; $i = $null; $i # -> 0, not $null
    
  • The conversion may fail, resulting in an error; in the case of [datetime], the conversion fails, because [datetime] $null fails.

    # !! ERROR: Cannot convert null to type "System.DateTime"
    [datetime] $dt = Get-Date; $dt = $null 
    

As for New-Object and assigning a non-null value to multiple variables:

As stated in the comments, the resulting behavior comes down to whether a value-type instance or a reference-type instance is being assigned[1]:

  • With value-type instances, each variable gets its own independent copy of the value.

    # A value-type instance such as an [int] instance assigns
    # *independent copies* of itself to the target variable(s):
    $var1 = $var2 = 42; $var2 = 43; $var1, $var2 # -> 42, 43
    
  • With reference-type instances, all variables truly reference the very same object.

    # A reference-type instance such as a [hashtable] instance assigns
    # a *reference to itself* to the target variable(s), which therefore
    # all reference the same object.
    $var1 = $var2 = @{one=1}; $var2.one = 2; $var1.one, $var2.one # -> 2, 2
    

Note that [string] instances are an exception: [string] is technically a reference type, but exhibits value-type semantics, so as to provide more intuitive string handling - see this answer.

New-Object is capable of instantiating either type: if you give it the name of a value type, you'll get a value-type instance; given the name of a reference type, you'll get a reference-type instance.

However, given that the built-in value types can be instantiated with literals (e.g., 1) or simple casts ([int]), New-Object is more typically used to instantiate reference types.


[1] Given a type or instance, you can inspect the type's .IsValueType property to determine if it is a value type or not; e.g., because [int] (System.Int32) is a value type [int].IsValueType and (1).GetType().IsValueType yield $true, whereas [System.IO.FileInfo].IsValueType is $false, because the latter is a reference type.

mklement0
  • 382,024
  • 64
  • 607
  • 775