首页 > 解决方案 > 如何提供 PowerShell 参数的自定义类型转换

问题描述

我有一个场景,我的 cmdlet 的参数之一是 CSV 文件名和列标题名称的元组。我想将其正确导入为 type 的元组[System.Tuple[System.IO.FileInfo,String]]。完整的代码如下所示:

[Parameter(Mandatory=$false)]
[ValidateScript( {
        if (-Not ($_.item1 | Test-Path) ) {
            throw "File or folder does not exist"
        }
        if (-Not ($_.item1 | Test-Path -PathType Leaf) ) {
            throw "The Path argument must be a file. Folder paths are not allowed."
        }
        return $true
    })]
[System.Tuple[System.IO.FileInfo,String]]$SomePath

但是,当您提供这样的参数时,-SomePath book1.csv,'Some Column Header'您会得到:

Invoke-MyCmdlet.ps1: Cannot process argument transformation on parameter 'SomePath'. Cannot convert the "System.Object[]" value of type "System.Object[]" to type "System.Tuple`2[System.IO.FileInfo,System.String]".

Microsoft 提供了有关此主题的一些文档,但老实说,我正在努力理解该示例中发生的事情。那里有很多我不熟悉的语法和命令,也不了解它们与类型转换过程的相关性。

我的问题是:有没有办法告诉 PowerShell 如何正确执行类型转换?如果是这样,它的语法是什么?它是否在其他地方记录得更清楚而我错过了?

标签: powershellparameterstype-conversion

解决方案


您可以实现并注册自己的PSTypeConverter(此处使用 PowerShell 类):

class CustomTupleConverter : System.Management.Automation.PSTypeConverter
{
  [bool]
  CanConvertFrom([object]$value, [type]$targetType)
  {
    # We only convert TO [Tuple[FileInfo,string]] 
    if($targetType -ne [System.Tuple[System.IO.FileInfo,string]]){
      return $false
    }

    # ... and only from 2-item arrays consisting of @([string],[string]) or @([FileInfo],[string])
    if($value -is [array] -and $value.Length -eq 2){
      if(($value[0] -is [string] -or $value[0] -is [System.IO.FileInfo]) -and $value[1] -is [string]){
        return $true
      }
    }

    return $false
  }

  [object]
  ConvertFrom([object]$value, [type]$targetType, [IFormatProvider]$format, [bool]$ignoreCase)
  {

    # Resolve individual values in the input array
    $fileInfo = if($value[0] -is [System.IO.FileInfo]){
      $value[0]
    }
    else{
      Get-Item -Path $value[0]
    }

    if($fileInfo -isnot [System.IO.FileInfo]){
      throw "Path didn't resolve to a file."
    }
    $headerName = $value[1] -as [string]

    # Create corresponding tuple and return
    return [System.Tuple]::Create($fileInfo, $headerName)
  }

  [bool]
  CanConvertTo([object]$value, [type]$targetType){
    return $this.CanConvertFrom($value, $targetType)
  }

  [object]
  ConvertTo([object]$value, [type]$targetType, [IFormatProvider]$format, [bool]$ignoreCase){
    return $this.ConvertFrom($value, $targetType, $format, $ignoreCase)
  }
}

定义后,我们需要将其注册为可能的类型转换器[System.Tuple[System.IO.FileInfo,string]]

$targetTypeName = [System.Tuple[System.IO.FileInfo,string]].FullName
$typeConverter = [CustomTupleConverter]
Update-TypeData -TypeName $targetTypeName -TypeConverter $typeConverter

现在我们可以将 2 个字符串绑定到一个元组:

function Test-TupleBinding
{
  param(
    [Parameter(Mandatory = $true)]
    [System.Tuple[System.IO.FileInfo,string]] $PathAndColumnName
  )

  $PathAndColumnName
}

参数绑定期间的转换魔法:

PS C:\> Test-TupleBinding -PathAndColumnName windows\system32\notepad.exe,ColumnNameGoesHere

Item1                           Item2              Length
-----                           -----              ------
C:\windows\system32\notepad.exe ColumnNameGoesHere      2

推荐阅读