【发布时间】:2018-06-15 20:40:01
【问题描述】:
我在编写的自定义函数中看到了一些奇怪的行为,因此我编写了一些具有不同特征的快速测试函数来展示这些行为。当参数集足够相似以至于唯一的区别因素是通过管道接收的对象的类型时,就会出现问题。
首先,我创建了一个与字符串不同的简单类型。
Add-Type @"
public class TestType {
public string Prop1;
}
"@
接下来,我创建了一个测试函数并使用字符串和 TestType 输入运行它。
function Test-ParameterSets1
{
[CmdletBinding()]
param (
[Parameter(Mandatory=$true, ValueFromPipeline=$true, ParameterSetName="Str")] [string] $StringInput,
[Parameter(Mandatory=$true, ValueFromPipeline=$true, ParameterSetName="Test")] [TestType] $TestInput
)
begin {
$result = New-Object Object | Select-Object –Property @{n='FunctionName';e={$PSCmdlet.MyInvocation.InvocationName}},@{n='ParameterSetName';e={$PSCmdlet.ParameterSetName}}
}
process {
$result | Add-Member -MemberType NoteProperty -Name StringInput -Value $StringInput -PassThru | Add-Member -MemberType NoteProperty -Name TestInput -Value $TestInput
}
end {
$result
}
}
'string' | Test-ParameterSets1
New-Object TestType | Test-ParameterSets1
FunctionName ParameterSetName StringInput TestInput
------------ ---------------- ----------- ---------
Test-ParameterSets1 __AllParameterSets string
Test-ParameterSets1 __AllParameterSets TestType
这是问题的核心。 ParameterSetName 的计算结果为 __AllParameterSets,即使从值中可以看出,参数已按预期设置。我的函数有很多参数集,并根据参数集进行大量切换以控制逻辑流程。
接下来我尝试添加一个参数集唯一的参数,正如预期的那样,ParameterSetName 对于仅指定它的调用是正确的。
function Test-ParameterSets2
{
[CmdletBinding()]
param (
[Parameter(Mandatory=$true, ValueFromPipeline=$true, ParameterSetName="Str")] [string] $StringInput,
[Parameter(Mandatory=$true, ValueFromPipeline=$true, ParameterSetName="Test")] [TestType] $TestInput,
[Parameter(ParameterSetName="Test")] [string] $TestName
)
begin {
$result = New-Object Object | Select-Object –Property @{n='FunctionName';e={$PSCmdlet.MyInvocation.InvocationName}},@{n='ParameterSetName';e={$PSCmdlet.ParameterSetName}}
}
process {
$result | Add-Member -MemberType NoteProperty -Name StringInput -Value $StringInput -PassThru | Add-Member -MemberType NoteProperty -Name TestInput -Value $TestInput
}
end {
$result
}
}
'string' | Test-ParameterSets2
New-Object TestType | Test-ParameterSets2 -TestName MyName
New-Object TestType | Test-ParameterSets2
FunctionName ParameterSetName StringInput TestInput
------------ ---------------- ----------- ---------
Test-ParameterSets2 __AllParameterSets string
Test-ParameterSets2 Test TestType
Test-ParameterSets2 __AllParameterSets TestType
接下来,我尝试添加一个对两个参数集都是必需的参数,这次 ParameterSetName 评估为一个空字符串,这尤其令人困惑。
function Test-ParameterSets5
{
[CmdletBinding()]
param (
[Parameter(Mandatory=$true, ValueFromPipeline=$true, ParameterSetName="Str")] [string] $StringInput,
[Parameter(Mandatory=$true, ValueFromPipeline=$true, ParameterSetName="Test")] [TestType] $TestInput,
[Parameter(Mandatory=$true, ParameterSetName="Str")] [Parameter(Mandatory=$true, ParameterSetName="Test")] [string] $Mandatory,
[Parameter(ParameterSetName="Test")] [string] $TestName
)
begin {
$result = New-Object Object | Select-Object –Property @{n='FunctionName';e={$PSCmdlet.MyInvocation.InvocationName}},@{n='ParameterSetName';e={$PSCmdlet.ParameterSetName}}
}
process {
$result | Add-Member -MemberType NoteProperty -Name StringInput -Value $StringInput -PassThru | Add-Member -MemberType NoteProperty -Name TestInput -Value $TestInput
}
end {
$result
}
}
'string' | Test-ParameterSets5 -Mandatory mandatoryParam
New-Object TestType | Test-ParameterSets5 -Mandatory mandatoryParam -TestName MyName
New-Object TestType | Test-ParameterSets5 -Mandatory mandatoryParam
FunctionName ParameterSetName StringInput TestInput
------------ ---------------- ----------- ---------
Test-ParameterSets5 string
Test-ParameterSets5 Test TestType
Test-ParameterSets5 TestType
似乎 PowerShell 实际上知道如何正确设置这些参数,但 ParameterSetName 没有正确评估。有没有办法让这个工作?我想避免使用它们自己的参数集所独有的不必要的开关,例如 -String 和 -TestType ,以便 PowerShell 可以完成它的工作。谢谢!
【问题讨论】:
-
如果在 cmdletBinding 的括号内添加 DefaultParameterSetName="Test" 或 "Str" 并设置您选择的 Position=0,结果是否相同?好问题,阅读这些内容后对结果感到好奇:blogs.msdn.microsoft.com/mediaandmicrocode/2009/04/10/… 和 blogs.msdn.microsoft.com/powershell/2009/04/06/… 和 blog.simonw.se/powershell-functions-and-parameter-sets
-
在函数能够看到第一个管道对象之前,您会在
begin块中获得ParameterSetName。您应该在process块中执行此操作。每个管道输入对象可以在单个函数调用中产生不同的ParameterSetName。 -
@PetSerAl 你是说整个参数需要在流程块中设置吗?很好奇
-
@trebleCode 我不明白你的问题。我所说的只是
process中的ParameterSetName可以返回与begin中的值相比不同的值,但OP 不会读取process块中的ParameterSetName。 -
class a { } class b { } class c { } function f { param([Parameter(ParameterSetName='a', ValueFromPipeline)][a]$a, [Parameter(ParameterSetName='b', ValueFromPipeline)][b]$b, [Parameter(ParameterSetName='c', ValueFromPipeline)][c]$c) begin { "ParameterSetName in begin block: $($PSCmdlet.ParameterSetName)" } process { "ParameterSetName in process block: $($PSCmdlet.ParameterSetName)" } } [a]::new(), [b]::new(), [c]::new() | f