PowerShell:带有验证的GetNewClosure()和Cmdlet [英] PowerShell : GetNewClosure() and Cmdlets with validation

查看:89
本文介绍了PowerShell:带有验证的GetNewClosure()和Cmdlet的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to understand how .GetNewClosure() works within the context of a script cmdlet in PowerShell 2.

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

In essence I have a function that returns an object like so:

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

..
}

提供的闭包似乎没有问题,我没有验证集选项。如果包含它,则新的关闭操作失败,并出现以下错误。

Providing I do not have the validate set options the closure appears to work fine. If it is included however the new closure fails with the following error.

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

Exception calling "GetNewClosure" with "0" argument(s): "Attribute cannot be added because it would cause the variable Options with value to become invalid."

大概是闭包试图捕获对Cmdlet的调用的上下文。由于参数 Options根本没有绑定,因此这与参数验证并不合适。

Presumably the closure is trying to capture the context of the call to the Cmdlet. Since the parameter "Options" is not bound at all this is not nicely with the parameter validation.

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

I imagine it's possible to avoid this by placing validation as code within the body of the Cmdlet instead of making use of the [Validate*()] decorators -- but this seems nasty and quite obscure. Is there a way of fusing these two ideas?

推荐答案

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

The "Attribute cannot be added" message is (or was) a PowerShell bug, I've submitted it to Microsoft with this bug report. That particular issue seems to have been fixed, (perhaps around V5.1. but anyone interested in Powershell Closures may still find info below interesting.

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

There is a workaround which works in earlier versions, but first here's a simplified repro case that produces the same error:

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

Test-ClosureWithValidation -Options Option1

解决方法取决于以下事实:GetNewClosure()通过遍历局部变量来工作在调用脚本的上下文中,将这些局部变量绑定到脚本的上下文中,该错误发生的原因是它复制了包含有效值的 $ Options 变量tion属性。您可以通过仅使用所需的局部变量来创建新上下文来解决该错误。在上面的简单复制中,这是一种单行解决方法:

The workaround depends on the fact that GetNewClosure() works by iterating over the local variables in the calling script's context, binding these local variables into the script's context. The bug occurs because its copying the $Options variable including the validation attribute. You can work around the bug by creating a new context with only the local variables you need. In the simple repro above, it is a one-line workaround:

    [scriptblock] $closure = &{ {"OK"}.GetNewClosure();}

上面的行现在创建了一个没有局部变量的范围。对于您的情况,这可能太简单了;如果需要外部作用域的某些值,则可以将它们复制到新作用域的局部变量中,例如:

The line above now creates a scope with no local variables. That may be too simple for your case; If you need some values from the outer scope, you can just copy them into local variables in the new scope, e.g:

    [scriptblock] $closure = &{ 
        $options = $options; 
        {"OK $options"}.GetNewClosure();
    }

请注意,以上第二行创建了一个新的$ options变量,将其分配给外部变量的 value ,属性不会传播。

Note that the second line above creates a new $options variable, assigning it the value of the outer variable, the attributes don't propagate.

最后,我不确定您的示例中为什么需要调用完全是GetNewClosure。变量$ this不是普通的局部变量,无论您是否创建闭包,该变量都可以在脚本属性中使用。示例:

Finally, I'm not sure in your example why you need to call GetNewClosure at all. The variable $this isn't a normal local variable, it will be available in your script property whether or not you create a closure. Example:

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

这篇关于PowerShell:带有验证的GetNewClosure()和Cmdlet的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆