1

假设您在一个模块(即一个.psm1文件)中定义了两个函数:

function f1{
    param($x1)
    $a1 = 10
    f2 $x1
}

function f2{
    param($x2)
    $a2 = 100
    & $x2
}

现在假设您运行以下命令:

PS C:\> $a0 = 1
PS C:\> $x0 = {$a0+$a1+$a2}
PS C:\> f1 $x0
1

$x2尽管在内部调用,但仍保留命令行的上下文$f2。如果您更改&..

在模块中替换$xn$xn.GetNewClosure()然后调用会f1捕获100但 not的值10

PS C:\> f1 $x0
101
PS C:\> f1 $x0.GetNewClosure
101

.GetNewClosure()发生这种情况是因为在内部调用f2“覆盖”了$a1捕获的值f1

有没有办法选择性地捕获脚本块中的变量?从这个例子开始,有没有办法同时捕捉$a1内部f1$a2内部f2


延伸阅读

PowerShell 作用域并不简单。考虑这个不完整的因素列表中的可能性:

  • 任何时候都可以有全局和模块范围层次结构的任意组合
  • .并且&调用对范围的影响不同,
  • 管道提供的复杂流控制意味着不同或相同范围层次结构的多个范围begin、、processend脚本块,甚至同一函数的多个调用可以同时处于活动状态

换句话说,PowerShell 作用域的工作描述抗拒简单性。

about_Scopes文档表明事情比实际上要简单得多。也许从这个问题分析和理解代码会导致更完整的理解。

4

1 回答 1

1

我希望有一种内置的方法可以实现这一点。我发现的最接近的东西是[scriptblock]::InvokeWithContext(). 手动处理参数InvokeWithContext()变得非常混乱。我设法通过在另一个模块中定义几个辅助函数来封装混乱:

function ConvertTo-xScriptblockWithContext{
    param([parameter(ValueFromPipeline=$true)]$InputObject)
    process{
        $InputObject | Add-Member -NotePropertyMembers @{variablesToDefine=@()}
        {$InputObject.InvokeWithContext(@{},$InputObject.variablesToDefine)}.GetNewClosure() |
        Add-Member -NotePropertyMembers @{ScriptBlockWithContext=$InputObject} -PassThru
}}  

function Add-xVariableToContext{
    param(
        [parameter(ValueFromPipeline=$true)]$InputObject,
        [parameter(position=1)]$Name,     
        [parameter(position=2)]$Value
    )
    process{
        $exists = $InputObject.ScriptBlockWithContext.variablesToDefine | ? { $_.Name -eq $Name }
        if ($exists) { $exists = $Value }
        else{ $InputObject.ScriptBlockWithContext.variablesToDefine += New-Object 'PSVariable' @($Name,$Value) }
}}

然后,f1f2脚本块通过时使用 Add-xVariableToContext 将变量添加到脚本块的上下文中:

function f1{
    param($x1)
    $a1 = 10
    $x1 | Add-xVariableToContext 'a1' $a1
    f2 $x1
}

function f2{
    param($x2)
    $a2 = 100
    $x2 | Add-xVariableToContext 'a2' $a2
    & $x2
}

请注意,$x2它像任何其他脚本块一样被调用,因此它可以安全地与任何接受脚本块的东西添加到其上下文中的变量一起使用。创建新的脚本块,添加$a0到它们的上下文,并将它们传递给f1看起来像这样:

$a0 = 1
$x0a,$x0b = {$a0+$a1+$a2},{$a0*$a1*$a2} | ConvertTo-xScriptblockWithContext
$x0a,$x0b | Add-xVariableToContext 'a0' $a0
f1 $x0a
f1 $x0b
#111
#1000
于 2015-03-02T22:53:46.127 回答