2

I expect that inside a class method, I can have "local" variable scoped inside of the method, but it is not the case. The following is the test code:

class test_scope {
  $var = "class var"

  test_scope() {
  }

  [void] my_method() {
    $var = "method var"
  }
}

$obj = [test_scope]::new()
$obj.my_method()

I got an error message:

Line |
   8 |      $var = "method var"
     |      ~~~~
     | Cannot assign property, use '$this.var'.

This is surprising. How can I have local variables?

As a comparison, function (outside of a class, that is) can have local variables with the same name as the script variable. The following is an example:

$var2="global var"
function my_function() {
  $var2="function var"
  write-host $var2
  write-host $script:var2
}
my_function($null)

and I got the answer I expected:

function var
global var

As another comparison with Python:

class test_scope:
  var = "class var"

  def my_method(self):
    var = "method var"
    print(self.var)
    print(var)

obj = test_scope()
obj.my_method()

It works as expected:

function var
global var

so PowerShell is out of norm?

PS: I tested under PowerShell 5.0 and 7.0.

puravidaso
  • 1,013
  • 1
  • 5
  • 22

2 Answers2

4

This is surprising. How can I have local variables?

You can have all the local variables you want in a class method - as long as their names don't collide with any class member name:

class MyClass
{
  [string]$var

  MyMethod()
  {
    $somethingOtherThanVar = "this should work"
  }
}

The reason PowerShell throws the error is a deliberate effort to be helpful - a class author who's new to the language or the class definition feature might assign to $var in a method, thinking that the $this.var (declared as $var outside the method body) would reflect the assignment and be in for a nasty surprise.

Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
2

The accepted answer avoids the problem but doesn't identify its root cause. This is that the scope of a variable must always be specified in class functions, with the exception of parameters that the function receives and class properties. So you can write without any problems:

class test_scope {
    $var = "class var"

    test_scope() {
    }

    [void] my_method() {
        $local:var = "local method var" # or private
        $script:var = "script var"      # or global

        # one can omit local or private scope when reading the variable:
        Write-Host "'$var' vs '$( $this.var )'"

        # one has to use global or script scope also when reading the variable:
        Write-Host "'$( $script:var )' vs '$( $this.var )'" # or global
    }
}

$obj = [test_scope]::new()
$obj.my_method()

Output:

'local method var' vs 'class var'
'script var' vs 'class var'
Olli
  • 327
  • 2
  • 9