Powershell:C#-style函数重载参数集?

时间:2012-01-18 18:51:07

标签: powershell parameters optional-parameters overloading

在C#中,函数重载历史上似乎如下所示,其中每个重载在更简单的签名之上添加了一些参数:

public void Initialize(int version);
public void Initialize(int version, string workspaceName);
public void Initialize(int version, string workspaceName, Path workspaceRoot, bool force);

在Powershell中,这些超载是不可能的;但是,通过System.Management.Automation.ParameterAttribute的ParameterSetName属性提供了函数重载的一些近似值。这允许我们将参数声明为某些参数集的成员,这允许我们有效地为我们的函数定义单独的签名。在大多数ParameterSetName examples中发现的简单案例是这样的:

function test-param 
{ 
[CmdletBinding(DefaultParametersetName="p2")] 
param( 
[Parameter(ParameterSetName="p1",Position=0)] 
[String] 
$d, 

[Parameter(ParameterSetName="p2", Position=0)] 
[String]$i 
) 
    switch ($PsCmdlet.ParameterSetName) 
{ 
    "p1"  { Write-Host ([DateTime]$d); break} 
    "p2"  { Write-Host ([INT]$i); break} 
    } 
}

获取此功能的帮助会产生以下结果:

PS D:\.ws> get-help test-param
test-param [[-i] <String>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
test-param [[-d] <String>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]

这种基本类型的示例对于参数集的复杂用法并不令人难以置信,因为参数集完全是互斥的。但是如果我们想让一些参数成为所有参数集的成员呢?更有经验的用户可能会建议这是特殊“AllParameterSets”参数集的情况,如果未指定参数集,则根据MSDN,该参数集是默认值。但是,请考虑以下事项:

PS D:\.ws>     function test-param 
{ 
    [CmdletBinding(DefaultParametersetName="p2")] 
    param
    ( 
        [Parameter(ParameterSetName="p1",Position=0)] 
        [String] 
        $d, 
        [Parameter(ParameterSetName="p2", Position=0)] 
        [String]$i,
        [Parameter(ParameterSetName="AllParameterSets")] 
        [String]$x
    ) 
}

PS D:\.ws> get-help test-param
test-param [[-i] <String>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
test-param [[-d] <String>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
test-param [-x <String>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]

显然,当“AllParameterSets”被指定为参数集时,它实际上并不像所期望的那样包含在所有参数集中。这可以通过省略声明来看出:

PS D:\.ws>     function test-param 
{ 
    [CmdletBinding(DefaultParametersetName="p2")] 
    param
    ( 
        [Parameter(ParameterSetName="p1",Position=0)] 
        [String] 
        $d, 
        [Parameter(ParameterSetName="p2", Position=0)] 
        [String]$i,
        [String]$x
    ) 
}

PS D:\.ws> get-help test-param
test-param [[-i] <String>] [-x <String>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
test-param [[-d] <String>] [-x <String>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]

现在,$ x参数出现在我们期望的所有函数签名中。

最后,最复杂的情​​况。如果我们希望一个参数出现在一些但不是所有签名中,该怎么办?原始示例中给出的workspaceName参数符合此描述。也许这个解决方案的关键是要意识到可以为param块中的每个参数声明多个Parameter属性。这允许我们为每个签名建立一个参数集,并使用它所属的每个集合的Parameter属性来装饰每个参数。请考虑以下事项:

PS D:\.ws> function Initialize-Something
{
    [CmdletBinding()]
    param
    (
        [Parameter(ParameterSetName="version")]
        [Parameter(ParameterSetName="workspaceName")]
        [Parameter(ParameterSetName="createWorkspace")]
        [int] $Version,
        [Parameter(ParameterSetName="workspaceName")]
        [Parameter(ParameterSetName="createWorkspace")]
        [string] $WorkspaceName,
        [Parameter(ParameterSetName="createWorkspace")]
        [string] $WorkspaceRoot,
        [Parameter(ParameterSetName="createWorkspace")]
        [switch] $Force
    )
}

PS D:\.ws> get-help initialize-something
Initialize-Something [-Version <Int32>] [-WorkspaceName <String>] [-WorkspaceRoot <String>] [-Force] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Initialize-Something [-Version <Int32>] [-WorkspaceName <String>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Initialize-Something [-Version <Int32>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]

PS D:\.ws> initialize-something -workspacename "test"
Initialize-Something : Parameter set cannot be resolved using the specified named parameters.
At line:1 char:21
+ initialize-something <<<<  -workspacename "test"
    + CategoryInfo          : InvalidArgument: (:) [Initialize-Something], ParameterBindingException
    + FullyQualifiedErrorId : AmbiguousParameterSet,Initialize-Something

Powershell声称它无法确定参数集,更可能是因为WorkspaceName出现在多个签名中。对于记录,当仅指定-Version时,或者对于除最复杂之外的任何签名时,也会发生这种情况。这可以通过[CmdletBinding(DefaultParameterSetName =“version”)]或其他东西来缓解,但它不是一个合适的解决方案。

所以在这之后,我的问题是:我怎样才能实现我正在寻找的那种签名?我是否需要创建一组过度显式的开关,如-VersionMode,-WorkspaceNameMode,-CreateWorkspaceMode,以指定我希望它运行的模式,从根本上否定参数集检测的优点?也许模式枚举?使用ParameterAttribute的Mandatory和Position属性可以通过一些优雅来完成吗?

谢谢!

2 个答案:

答案 0 :(得分:5)

如果您试图模拟C#函数重载,最简单的方法是简单地允许“可选”参数,例如:

function Initialize([int]$version, [string]$workspaceName, [string]workspaceRoot, [switch]Force)

调用者未指定的参数将默认为$ null(开关默认为$ false)。您还可以为参数提供默认值,如下所示:

function Initialize([int]$version, [string]$workspaceName = $(<script here>), [string]workspaceRoot, [switch]Force)

答案 1 :(得分:1)

有关参数集的两个重要事项是,Windows PowerShell运行时仅为特定输入使用一个参数集,并且每个参数集必须至少有一个对该参数集唯一的参数。

在您的情况下,您需要指定第二个参数,例如-WorkspaceRoot,它将起作用:

initialize-something -workspacename "test" -WorkspaceRoot "test2"