【问题标题】:Function overloading in PowerShellPowerShell 中的函数重载
【发布时间】:2011-04-25 20:08:10
【问题描述】:

你能在 PowerShell 中重载函数吗?

我想让我的函数接受一个字符串、数组或一些开关。

我想要的一个例子:

  • Backup-UsersData singleUser
  • 备份-UsersData @('Alice', 'Bob', “乔”)
  • 备份-UsersData -all

【问题讨论】:

  • “参数集”基本上相当于OO方法重载。由于 poewrshell 的类型强制繁重的解析器,它们有时会有点棘手。

标签: function powershell parameters overloading


【解决方案1】:

在 PowerShell 中函数没有重载。最后一个定义覆盖同一范围内的前一个或隐藏父范围内的前一个。因此,您应该创建一个函数并提供一种通过参数来区分其调用模式的方法。

在 V2 中,您可以使用高级功能,请参阅 help about_Functions_Advanced_Parameters 并避免在解决参数集歧义时进行一些手动编码:

# advanced function with 3 parameter sets
function Backup-UsersData
(
    [Parameter(Position=0, ParameterSetName="user")]
    [string]$user,
    [Parameter(Position=0, ParameterSetName="array")]
    [object[]]$array,
    [Parameter(Position=0, ParameterSetName="all")]
    [switch]$all
)
{
    # use this to get the parameter set name
    $PSCmdlet.ParameterSetName
}

# test
Backup-UsersData -user 'John'
Backup-UsersData 1, 2
Backup-UsersData -all

# OUTPUT:
# user
# array
# all

请注意,这种机制有时很奇怪。例如,在第一个测试中,我们必须明确指定参数名称-user。否则:

Backup-UsersData : Parameter set cannot be resolved using the specified named parameters.
At C:\TEMP\_101015_110059\try2.ps1:21 char:17
+ Backup-UsersData <<<<  'John'
    + CategoryInfo          : InvalidArgument: (:) [Backup-UsersData], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : AmbiguousParameterSet,Backup-UsersData

在许多情况下,标准而非高级的混合参数函数可以:

function Backup-UsersData
(
    [string]$user,
    [object[]]$array,
    [switch]$all
)
{
    if ($user) {'user'}
    elseif ($array) {'array'}
    elseif ($all) {'all'}
    else {'may be'}
}

Backup-UsersData -user 'John'
Backup-UsersData -array 1, 2
Backup-UsersData -all
Backup-UsersData

但在这种情况下,您应该解决(或接受并忽略)歧义,例如决定做什么,如果说:

Backup-UsersData -user 'John' -array 1, 2 -all

【讨论】:

  • 需要说明的是类函数可以重载。这与在其他语言中的完成方式类似,通过声明具有相同名称但参数类型不同的多个函数来完成。例如,具有函数[string] val([int] $i) { return "Int: $i" }[string] val([string] $s) { return "String: $s" } 的类将输出Int: 1 对应$t.val(1)String: abc 对应$t.val('abc')注意:我已验证此代码使用 PowerShell 5.1 和 7.1)
  • 另外应该注意的是,Powershell(在 5.1 上,可能更早)可以并将单个值强制转换为 [Object[]] 参数:该函数将获取一个以原始对象作为其单个值的数组。这适用于任何类型,只要单个值可以强制转换为数组包含的类型(即"plop" 可以强制转换为[String[]],但不能转换为[Int32[]])。
【解决方案2】:

这是 Roman 的答案的一个变体,我认为它更灵活一些:

function Backup
{
    [CmdletBinding(DefaultParameterSetName='Users')]
    Param (
        [parameter(mandatory=$true, ParameterSetName='Users', position=0, ValueFromPipeline=$true)][string[]]$User,
        [parameter(mandatory=$true, ParameterSetName='AllUsers')][switch]$All
    )

    Begin
    {
        if ($All) { $User = @('User1', 'User2', 'User3') }
    }

    Process
    {
        foreach ($u in $User)
        {
            echo "Backup $u"
        }
    }
}

【讨论】:

    【解决方案3】:

    1) 建立一个类...

    class c1 { 
        [int]f1( [string]$x ){ return 1 } 
        [int]f1( [int ]$x ){ return 2 }
        }
    

    1+) 如果您希望在不实例化的情况下调用它们,请使用静态方法...

    class c1 { 
        static [int] f1( [string]$x ){ return 1 } 
        static [int] f1( [int]$x ){ return 2 } 
        }
    

    2) 调用类或对象中的方法...重载工作正常

    $o1 = [c1]::new()
    o1.f1( "abc" ) ~> returns 1
    o1.f1( 123 )   ~> returns 2
    

    -或-


    [c1]::f1( "abc" ) ~> returns 1
    [c1]::f1( 123 )   ~> returns 2
    

    3) 如果(像我一样)
    您希望将“重载函数”放置在库中...
    这样您的用户就可以透明地使用它们...
    来自代码或来自交互式命令行 (REPL)...

    我能到的最接近的地方
    “Powershell 中的重载函数”
    是这样的:

    function Alert-String() { [c1]::f1( "abc" ) }
    function Alert-Strings(){ [c1]::f1( 123 ) }
    function Alert-Stringn(){ [c1]::f1( 123 ) }
    

    也许在 PS-Core v8 中??? ;-)

    希望对你有帮助……

    【讨论】:

      【解决方案4】:

      如果您使用 PSObject 而不是 Object 来定义参数类型,它应该可以工作。 例如Get-Control函数,知道如何根据类型字符串或模板进行重载,可以使用位置值调用:

          Get-Control "A-Name-Of-A-Control"
          Get-Control $template
      

      要使重载工作,请按如下方式使用 PSObject:

      Function Get-Control {
          Param(
              [Parameter(Mandatory=$False,ParameterSetName="ByTemplate",Position=0)]
              [PSObject]
              $Template,
      
              [Parameter(Mandatory=$False,ParameterSetName="ByName",Position=0)]        
              [String]
              $Name,
      
              [Parameter(Mandatory=$False)] 
              [Switch]
              $List
            ) 
         ... # remaining code removed for brevity
      

      【讨论】:

        猜你喜欢
        • 2020-09-13
        • 1970-01-01
        • 2012-02-13
        • 2021-01-02
        • 1970-01-01
        • 1970-01-01
        • 2014-06-18
        • 2018-09-18
        • 2011-10-30
        相关资源
        最近更新 更多