使用管道vs命名参数传递时,为什么对象的处理方式不同?

时间:2014-12-14 22:39:41

标签: .net powershell parameter-passing cmdlets

我天真地假设使用PowerShell的管道参数绑定传递给命令行开关的对象与使用命名参数传递它的方法相同。但是,以下脚本似乎表明存在差异:

Function Process-xMsmqQueue{
    [CmdletBinding()]
    Param(
    [Parameter()]$obj,
    [Parameter(
        ValueFromPipeline=$True,
        ValueFromPipelineByPropertyName=$True)]
    [System.Messaging.MessageQueue]$queue
    )
    Write-Host $obj
}

New-MsmqQueue 'xTest1' -ErrorAction Ignore
[System.Reflection.Assembly]::LoadWithPartialName('System.Messaging') | Out-Null
New-Object System.Messaging.MessageQueue('.\private$\xTest1') -OutVariable q
$first = 'first'
$second = 'second'
$q | Process-xMsmqQueue -obj $first
Process-xMsmqQueue -queue $q -obj $second 

我希望此脚本能够打印firstsecond。相反,它会在最后一行抛出ParameterArgumentTransformationError

enter image description here

$queue参数的处理差异似乎只显示某些对象类型。例如,将[System.Messaging.MessageQueue]替换为[System.String]并将字符串传递给$queue不会产生任何错误。

我的问题:

  1. 为什么System.Messaging.MessageQueue对象在使用管道传递而不是命名参数时会被区别对待?
  2. 为什么System.String不受差异影响?
  3. 如何更改此脚本以使-queue的命名参数传递工作?

1 个答案:

答案 0 :(得分:1)

作为@mikez pointed out,一旦你意识到-OutVariable产生了一个对象的ArrayList,那么这三个问题的答案就很明显了。 -Outvariable适合管道和seems to exist to facilitate inspection of objects passed through the pipeline进一步消费。

问题1

  

为什么System.Messaging.MessageQueue对象的处理方式不同   使用管道与命名参数传递时?

考虑以下一行:

New-Object System.Messaging.MessageQueue('.\private$\xTest1') -OutVariable q

实际上会生成System.Messaging.MessageQueue[](不是System.Messaging.MessageQueue)类型的对象,它是MessageQueues的Arraylist。

<强>管道

考虑这一行:

$q | Process-xMsmqQueue -obj $first

据我了解,Powershell解释了管道运算符,以便后续的命令行开关作用于Arraylist中的每个元素。鉴于上下文,该行等同于以下内容:

foreach($item in $q){
    Process-xMsmqQueue -queue $item -obj $first
}

并且,因为$q只包含一个对象,foreach的正文只执行一次。

命名参数

既然我们知道$q实际上是System.Messaging.MessageQueue个对象的Arraylist,那么以下行会产生错误就不足为奇了:

Process-xMsmqQueue -queue $q -obj $second

-queue接受System.Messaging.MessageQueue,但$q属于Arraylist类型。


旁注:让我误入歧途的是$q | gm输出TypeName: System.Messaging.MessageQueue。但是,$q.GetType()会返回ArrayList。看看Get-Member文档解释了这种差异:

  

当您将对象集合传递给Get-Member时,Get-Member会获得   集合中各个对象的成员,例如   字符串数组中每个字符串的属性。


问题2

  

为什么System.String不受差异影响?

如果你以相同的方式获得一个字符串:

New-Object System.String('MyString') -OutVariable $str

$strSystem.String个对象的ArrayList。如果我将其按名称传递给期望System.String的参数,我会得到相同的ParameterArgumentTransformationError

问题3

  

如何更改此脚本以使命名参数传递为-queue?

有几个选择:

  1. 使用=分配New-Object的输出:$q=New-Object ...这种方式$q包含单个对象,而不是-OutVariable带来的ArrayList

  2. Process-xMsmqQueue -queue更改为接受System.Messaging.MessageQueue[]。命令行开关需要迭代ArrayList。

  3. 索引$q,如下所示:Process-xMsmqQueue -queue $q[0]这将获取由New-Object生成的ArrayList的第一个元素