powershell 如何声明/使用强类型 predicate 函数作为Cmdlet参数?

xj3cbfub  于 2023-05-17  发布在  Shell
关注(0)|答案(1)|浏览(193)

我找到了许多关于如何使用script block作为Cmdlet参数的答案,但我找不到关于如何声明和使用强类型 predicate 函数的答案。类似于.NET Func<T,TResult>委托。
我尝试了以下方法,但没有效果:

function Test-Execution
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)][int]$myValue,
        [Parameter(Mandatory = $true, Position = 1)][Func[int, bool]]$myFunc
    )
    process
    {
        if ($myFunc.Invoke($myValue)) { 'Yes' }
        else { 'No' }
    }
}

function Test-Predicate
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true, Position = 0)][int]$myValue
    )
    process
    {
        $myValue -lt 3
    }
}

1..5 | Test-Execution -myFunc Test-Predicate
qyswt5oh

qyswt5oh1#

你并不需要一个 predicate ,只需要一个脚本块就可以了,但是在这个例子中,你只需要传递一个脚本块作为Func<T, TResult>参数,而不是传递你的函数名,然后这个脚本块被强制到你的 predicate 中。
请注意,你的问题中的 predicate 当前以bool作为输入并输出int32,我相信你正在寻找相反的方式,因此[Func[int, bool]]而不是[Func[bool, int]]

function Test-Execution {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
        [int] $myValue,

        [Parameter(Mandatory = $true, Position = 1)]
        [Func[int, bool]] $myFunc
    )

    process {
        if ($myFunc.Invoke($myValue)) { 'Yes' }
        else { 'No' }
    }
}

1..5 | Test-Execution -myFunc { $myValue -lt 3 }

此外,即使这样做有效,实际上应该在 predicate 中使用$args[0]而不是$myValue进行计算,因为$args[0]代表传递给 predicate 的第一个参数:

1..5 | Test-Execution -myFunc { $args[0] -lt 3 }

如果你想使用$_而不是$args[0]来类似于 * 通过管道传递的当前对象 *,你可以使用调用操作符&,但在这种情况下,你的函数只能在管道中工作:

function Test-Execution {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
        [int] $myValue,

        [Parameter(Mandatory = $true, Position = 1)]
        [scriptblock] $myFunc
    )

    process {
        if (& $myFunc) {
            return 'Yes'
        }

        'No'
    }
}

1..5 | Test-Execution -myFunc { $_ -lt 3 }

例如,即使函数没有从管道接收输入,也可以使用$_作为参数进行计算,使用InvokeWithContext(注意,这里我们将输入对象更改为[int[]] $myValue并添加一个循环):

function Test-Execution {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
        [int[]] $myValue,

        [Parameter(Mandatory = $true, Position = 1)]
        [scriptblock] $myFunc
    )

    process {
        foreach($value in $myValue) {
            if($myFunc.InvokeWithContext($null, [psvariable]::new('_', $value)[0])) {
                'Yes'
                continue
            }

            'No'
        }
    }
}

# now both ways work using this method, positional binding:
Test-Execution (1..5) -myFunc { $_ -lt 3 }

# and pipeline processing:
1..5 | Test-Execution -myFunc { $_ -lt 3 }

相关问题