我试图了解.GetNewClosure()在PowerShell 2中的脚本cmdlet上下文中如何工作。

本质上,我有一个返回像这样的对象的函数:

function Get-AnObject {
param(
    [CmdletBinding()]
    [Parameter(....)]
    [String[]]$Id
    ..
    [ValidateSet('Option1','Option2')]
    [String[]]$Options
)

...

    $T = New-Object PSCustomObject -Property @{ ..... }
    $T | Add-Member -MemberType ScriptProperty -Name ExpensiveScriptProperty -Value {
        $this | Get-ExpensiveStuff
    }.GetNewClosure()

..
}

如果我没有验证集选项,则关闭似乎可以正常工作。如果包含它,则新的关闭失败并显示以下错误。

“使用” 0“参数调用” GetNewClosure“的异常:”无法添加属性,因为这将导致带有值的变量Options变为无效。“

大概闭包正在尝试捕获对Cmdlet的调用的上下文。由于参数“Options”根本没有绑定(bind),因此在参数验证中效果不佳。

我想可以通过将验证作为代码放置在Cmdlet的正文中而不是使用[Validate *()]装饰器来避免这种情况,但这似乎很讨厌并且晦涩难懂。有没有办法融合这两个想法?

最佳答案

“无法添加属性”消息是(或曾经)是一个PowerShell错误,我已使用this bug report将其提交给Microsoft。该特定问题似乎已得到解决,(也许在V5.1左右。但是对Powershell封闭感兴趣的任何人仍然可以在下面找到有趣的信息。

有一种解决方法可以在早期版本中使用,但是首先这是一个简化的repro案例,该案例会产生相同的错误:

function Test-ClosureWithValidation {
    [CmdletBinding()]
    param(
        [Parameter()]
        [ValidateSet('Option1','Option2')]
        [String[]]$Options
    )
    [scriptblock] $closure = {"OK"}.GetNewClosure();
    $closure.Invoke()
}

Test-ClosureWithValidation -Options Option1

解决方法取决于以下事实:GetNewClosure()通过在调用脚本的上下文中迭代局部变量并将这些局部变量绑定(bind)到脚本的上下文中而起作用。发生该错误是因为其复制了包含验证属性的$ Options变量。您可以通过仅使用所需的局部变量创建新的上下文来解决该错误。在上面的简单复制中,这是一种单行解决方法:
    [scriptblock] $closure = &{ {"OK"}.GetNewClosure();}

现在,上面的行创建了一个没有局部变量的范围。对于您的情况,这可能太简单了;如果您需要外部作用域中的某些值,则可以将它们复制到新作用域中的局部变量中,例如:
    [scriptblock] $closure = &{
        $options = $options;
        {"OK $options"}.GetNewClosure();
    }

请注意,上面的第二行创建了一个新的$ options变量,为其分配了外部变量的值,这些属性不会传播。

最后,我不确定您的示例中为什么根本需要调用GetNewClosure。变量$ this不是普通的局部变量,无论您是否创建闭包,该变量都将在您的脚本属性中可用。例:
function Test-ScriptPropertyWithoutClosure {
    [CmdletBinding()]
    param(
        [Parameter()]
        [ValidateSet('Option1','Option2')]
        [String[]]$Options
    )
    [pscustomobject]@{ Timestamp= Get-Date} |
        Add-Member ScriptProperty ExpensiveScriptProperty {
            $this | get-member -MemberType Properties| % Name
        } -PassThru
}

Test-ScriptPropertyWithoutClosure -Options Option1 | fl

08-25 06:26