2

以下 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,因为 的值$xGetNewClosure调用捕获。一切如预期。

printx但是:来自闭包输出的调用in printx: x=4。因此,在其中printx执行的范围不受 where 闭包范围的影响$x = 3

我觉得奇怪的是,从普通脚本块调用的函数确实看到了脚本块范围内的变量,但从闭包调用的函数却看不到闭包中的变量。

4

1 回答 1

10

考虑以下代码:

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:您从全局范围开始,在其中定义一些变量。
图片1
图 2: GetNewClosure()创建新模块并将变量复制到其中。(红色箭头表示父作用域关系)
图二
图3:你改变变量的值。模块范围不受影响。
图三
图 4:调用的函数Run。它创建局部变量$a(蓝色箭头表示调用方向)
图4
图5: $SB调用的脚本块。脚本块绑定到模块会话状态,所以你过渡到它。
图5
图 6:调用的函数Print。函数绑定到全局会话状态,所以你返回到它。
图 6

于 2016-01-26T11:55:52.737 回答