Есть ли способ выборочного захвата переменных в контексте блока скрипта внутри модулей?

Предположим, вы определили две функции в модуле (т.е. .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

Это происходит потому, что звонит .GetNewClosure() внутри f2 "перезаписывает" значение $a1 захвачен в f1,

Есть ли способ выборочного захвата переменных в скрипт-блоках? Работая на примере, есть ли способ захватить оба $a1 внутри f1 а также $a2 внутри f2?

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) }
}}

Затем, 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
Другие вопросы по тегам