2

考虑以下脚本:

function g
{
    [CmdletBinding()]
    param
    (
        [parameter(ValueFromPipelineByPropertyName = $true)]$x,
        [parameter(ValueFromPipelineByPropertyName = $true)]$y,
        [parameter(ValueFromPipelineByPropertyName = $true)]$z
    )
    process
    {
        $retval = @{psbp=@{};mibp=@{};x=$x;y=$y;z=$z}
        $PSBoundParameters.Keys |
            % { $retval.psbp.$_ = $PSBoundParameters.$_ }
        $PSCmdlet.MyInvocation.BoundParameters.Keys |
            % { $retval.mibp.$_ = $PSCmdlet.MyInvocation.BoundParameters.$_} 
        return New-Object psobject -Property $retval
    }
}

$list = (New-Object psobject -Property @{x=1;z=3}),
        (New-Object psobject -Property @{x=$null;y=2} ) 
$list | 
    g |
    Select bp,x,y,z |
    ft -AutoSize

运行脚本会产生以下输出:

psbp      mibp      x y z
----      ----      - - -
{z, x}    {z, x}    1   3
{y, z, x} {y, z, x}   2 

这似乎表明两者都$PSBoundParameters包含$PSCmdlet.MyInvocation.BoundParameters到目前为止绑定的所有参数的累积。

我相当确定,$x并且$z在第一步绑定,并且在第二步$x绑定$y,但我还没有找到以编程方式检索该详细信息的方法。

如何确定仅在当前管道步骤中绑定的参数?


我为什么要关心这个?某些类型的参数验证比使用参数集等语言功能实现的要复杂得多ValidateScript()。必须在函数体内执行该验证。有时需要考虑将未绑定参数与传递$null给同一参数的语义不同。绑定参数的检测通常是通过询问来实现的$PSBoundParameters。如果您在管道上仅传递一个参数对象,则此方法可以正常工作。但是,如果您使用管道传递参数对象列表,则由于上述脚本演示的问题,该检测会失败。这违反了最小意外原则,因为在foreach当调用者碰巧通过管道将相同的对象传递给它来调用它时,循环的行为会大不相同。

可以通过在 a 中调用受影响的函数来foreach解决这个问题,但我宁愿解决所有调用的问题,也不愿完全放弃管道。

4

1 回答 1

2

您可以记住块中命令行限制的所有参数begin,然后在块的末尾process您可以清除$PSBoundParameters当前输入对象限制的参数:

function g
{
    [CmdletBinding()]
    param
    (
        [parameter(ValueFromPipelineByPropertyName = $true)]$x,
        [parameter(ValueFromPipelineByPropertyName = $true)]$y,
        [parameter(ValueFromPipelineByPropertyName = $true)]$z
    )
    begin
    {
        $CommandLineBoundParameters=@($PSBoundParameters.Keys)
    }
    process
    {
        ...

        @($PSBoundParameters.Keys) |
            ? { $CommandLineBoundParameters -notcontains $_ } |
            % { [void]$PSBoundParameters.Remove($_) }
    }
}
于 2015-12-02T02:26:10.103 回答