解释从PowerShell闭包中调用的函数的范围

时间:2016-01-26 09:13:19

标签: powershell closures scriptblock

以下PowerShell代码显示从闭包调用的函数的意外范围行为。你能解释一下这是"设计"还是缺陷?

function runblock($block) {
    $x = 4
    & $block
}

function printx() {
    "  in printx: x=" + $x
}

"PSVersion $($PSVersionTable.PSVersion)"

$x = 1
$b = {"In block x=" + $x ; $x = 3 ; printx}
$x = 2
runblock $b

$x = 1
$b = {"In closure x=" + $x ; $x = 3 ; printx}.GetNewClosure()
$x = 2
runblock $b

以上输出是

PSVersion 3.0
In block x=4
  in printx: x=3
In closure x=1
  in printx: x=4

大部分输出对我有意义:

脚本块输出In block x=4,因为其父作用域是runblock函数。 printx函数输出x=3,因为其父作用域是脚本块作用域。

关闭输出In closure x=1,因为$x调用捕获了GetNewClosure的值。一切如预期。

BUT: 从闭包中调用printx输出in printx: x=4。因此printx执行的范围不受$x = 3的闭包范围的影响。

对我来说,从普通脚本块调用的函数确实可以看到脚本块作用域中的变量,但是从闭包中调用的函数看不到闭包中的变量,这似乎很奇怪。

1 个答案:

答案 0 :(得分:8)

请考虑以下代码:

function Run {
    param($ScriptBlock)
    $a = 3
    # Picture 4
    & $ScriptBlock
}
function Print {
    # Picture 6
    "Print `$a=$a"
    "Print `$b=$b"
}
$a = 1
$b = 1
# Picture 1
$SB = {
    # Picture 5
    "Closure `$a=$a"
    "Closure `$b=$b"
    Print
}.GetNewClosure()
# Picture 2
$a = 2
$b = 2
# Picture 3
Run $SB

打印:

Closure $a=1
Closure $b=1
Print $a=3
Print $b=2

图片1:您从全局范围开始,您可以在其中定义一些变量 Picture 1
图片2 GetNewClosure()创建新模块并将变量复制到其中。 (红色箭头显示父范围关系)
Picture 2
图3:您更改了变量的值。模块范围不受影响。
Picture 3
图片4:函数Run被调用。它创建局部变量$a(蓝色箭头显示呼叫方向)
Picture 4
图片5: $SB脚本块被调用。脚本块绑定到模块会话状态,因此您转移到它 Picture 5
图片6:函数Print已调用。函数绑定到全局会话状态,因此返回到它 Picture 6