>假设您在模块中定义了两个函数(即.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
的值,但不会捕获10
的值:
PS C:> f1 $x0
101
PS C:> f1 $x0.GetNewClosure
101
发生这种情况是因为在f2
内调用.GetNewClosure()
会"覆盖"f1
中捕获的$a1
的值。
有没有办法有选择地捕获脚本块中的变量? 从示例中开始,有没有办法同时捕获f1
内部的$a1
和f2
内部的$a2
?
延伸阅读
PowerShell示波器并不简单。 考虑一下这个不完整的因素列表中的可能性:
- 可以随时激活全局和模块范围层次结构的任意组合
-
.
和&
调用对范围的影响不同,
流 - 水线提供的复杂流控制意味着不同或相同作用域层次结构的
begin
、process
和end
脚本块的多个作用域,甚至同一函数的多个调用都可以同时处于活动状态
换句话说,PowerShell范围的工作描述拒绝简单性。
about_Scopes文件表明,事情远比实际情况简单得多。 也许从这个问题中分析和理解代码会导致更完整的理解。
有一种内置的方法来实现这一点。 我发现最接近的东西是[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) }
}}
然后,f1
并f2
在脚本块通过时使用 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