Powershell lambda 作用域
Powershell lambda scope
我正在尝试使用高阶函数来执行某项任务,但范围似乎没有像我预期的那样工作。代码如下:
function DoSomething($scriptBlock, $message) {
# if not only running a check, run the given code
if ($shouldRunCheck) {
return $scriptBlock.Invoke()
}
Write-Host $message -foreground cyan
# write $message to file
}
这似乎工作正常,但当我调用它时,我似乎无法保存到脚本块之外的变量。
$myArray = @()
$myArray += 'test 1'
DoSomething {
write-host $myArray # test 1
$myArray += 'test 2'
write-host $myArray # test 2
}
write-host $myArray # test 1
$myArray += 'test 3'
write-host $myArray # test 1 test 3
本质上我需要从回调函数中添加到数组变量,但它似乎覆盖了变量,就好像变量是只读的一样?
你对 Scoping 的看法是正确的。
发生的事情是在脚本块内,当执行 $myArray += 'test 2'
时,变量 $myArray
被创建为 [String] 类型。这个新变量在函数外不存在,所以原来的$myArray
根本没有改变。
为此,您需要在 $myArray
上使用范围界定并将其声明为 $script:myArray = @('test 1')
,以便可以在整个脚本中访问它。
然后在脚本块中使用 $script:myArray += 'test 2'
添加新值,例如:
function DoSomething($scriptBlock) {
return $scriptBlock.Invoke()
}
$script:myArray = @('test 1') # declared in script-scope
# while inside the main script, you can simply access it as $myArray
Write-Host "MainScript: $myArray" # --> test 1
$scriptBlock = {
Write-Host "ScriptBlock: $script:myArray" # --> test 1
$script:myArray += 'test 2'
Write-Host "ScriptBlock: $script:myArray" # --> test 1 test 2
}
DoSomething $scriptBlock
Write-Host "MainScript: $myArray" # --> test 1 test 2
$myArray += 'test 3'
Write-Host "MainScript: $myArray" # --> test 1 test 2 test 3
结果:
MainScript: test 1
ScriptBlock: test 1
ScriptBlock: test 1 test 2
MainScript: test 1 test 2
MainScript: test 1 test 2 test 3
+1
对于 @Theo 的解释,尽管我会尽量避免使用 $Global:
和 $Script:
范围,因为它们可能会造成混淆。
也可以使用 ([Ref]$myArray).Value
:
来引用调用者的当前范围
function DoSomething($scriptBlock) {
return $scriptBlock.Invoke()
}
$myArray = @('test 1')
$scriptBlock = {
([Ref]$myArray).Value += 'test 2'
}
DoSomething $scriptBlock
$myArray += 'test 3'
Write-Host $myArray
test 1 test 2 test 3
但是 Theo 的解释也暗示在 PowerShell 数组上使用 +=
是一个坏主意,因为您每次都将重新创建数组,这很慢。
换句话说,为了获得更好的性能,最好使用 ArrayList
和 Add
方法而不是分配项目:
function DoSomething($scriptBlock) {
return $scriptBlock.Invoke()
}
$myArray = New-Object System.Collections.ArrayList
$myArray.Add('test 1')
$scriptBlock = {
$myArray.Add('test 2')
}
DoSomething $scriptBlock
$myArray.Add('test 3')
Write-Host $myArray
test 1 test 2 test 3
您还可以考虑使用强大的 PowerShell 管道来执行以下操作:
$scriptBlock = {
'test 2'
}
$myArray = @(
'test 1'
DoSomething $scriptBlock
'test 3'
)
Write-Host $myArray
test 1 test 2 test 3
我正在尝试使用高阶函数来执行某项任务,但范围似乎没有像我预期的那样工作。代码如下:
function DoSomething($scriptBlock, $message) {
# if not only running a check, run the given code
if ($shouldRunCheck) {
return $scriptBlock.Invoke()
}
Write-Host $message -foreground cyan
# write $message to file
}
这似乎工作正常,但当我调用它时,我似乎无法保存到脚本块之外的变量。
$myArray = @()
$myArray += 'test 1'
DoSomething {
write-host $myArray # test 1
$myArray += 'test 2'
write-host $myArray # test 2
}
write-host $myArray # test 1
$myArray += 'test 3'
write-host $myArray # test 1 test 3
本质上我需要从回调函数中添加到数组变量,但它似乎覆盖了变量,就好像变量是只读的一样?
你对 Scoping 的看法是正确的。
发生的事情是在脚本块内,当执行 $myArray += 'test 2'
时,变量 $myArray
被创建为 [String] 类型。这个新变量在函数外不存在,所以原来的$myArray
根本没有改变。
为此,您需要在 $myArray
上使用范围界定并将其声明为 $script:myArray = @('test 1')
,以便可以在整个脚本中访问它。
然后在脚本块中使用 $script:myArray += 'test 2'
添加新值,例如:
function DoSomething($scriptBlock) {
return $scriptBlock.Invoke()
}
$script:myArray = @('test 1') # declared in script-scope
# while inside the main script, you can simply access it as $myArray
Write-Host "MainScript: $myArray" # --> test 1
$scriptBlock = {
Write-Host "ScriptBlock: $script:myArray" # --> test 1
$script:myArray += 'test 2'
Write-Host "ScriptBlock: $script:myArray" # --> test 1 test 2
}
DoSomething $scriptBlock
Write-Host "MainScript: $myArray" # --> test 1 test 2
$myArray += 'test 3'
Write-Host "MainScript: $myArray" # --> test 1 test 2 test 3
结果:
MainScript: test 1 ScriptBlock: test 1 ScriptBlock: test 1 test 2 MainScript: test 1 test 2 MainScript: test 1 test 2 test 3
+1
对于 @Theo 的解释,尽管我会尽量避免使用 $Global:
和 $Script:
范围,因为它们可能会造成混淆。
也可以使用 ([Ref]$myArray).Value
:
function DoSomething($scriptBlock) {
return $scriptBlock.Invoke()
}
$myArray = @('test 1')
$scriptBlock = {
([Ref]$myArray).Value += 'test 2'
}
DoSomething $scriptBlock
$myArray += 'test 3'
Write-Host $myArray
test 1 test 2 test 3
但是 Theo 的解释也暗示在 PowerShell 数组上使用 +=
是一个坏主意,因为您每次都将重新创建数组,这很慢。
换句话说,为了获得更好的性能,最好使用 ArrayList
和 Add
方法而不是分配项目:
function DoSomething($scriptBlock) {
return $scriptBlock.Invoke()
}
$myArray = New-Object System.Collections.ArrayList
$myArray.Add('test 1')
$scriptBlock = {
$myArray.Add('test 2')
}
DoSomething $scriptBlock
$myArray.Add('test 3')
Write-Host $myArray
test 1 test 2 test 3
您还可以考虑使用强大的 PowerShell 管道来执行以下操作:
$scriptBlock = {
'test 2'
}
$myArray = @(
'test 1'
DoSomething $scriptBlock
'test 3'
)
Write-Host $myArray
test 1 test 2 test 3