PowerShell Where-Object 与 Where 方法

Ily*_*lya 6 powershell

我注意到编写 PowerShell 类行时发生了一件有趣而奇怪的事情:

class A {

    [object] WhereObject(){
        return @(1,2) | Where-Object {$_ -gt 2}
    }

    [object] Where(){
        return @(1,2).Where( {$_ -gt 2})
    }
}

$a = new-object A
$a.WhereObject() # Throw exception Index was out of range. Must be non-negative and less than the size of the collection.

$a.Where() # Works well
Run Code Online (Sandbox Code Playgroud)

看起来它是设计使然。为什么会这样?

解决方法

将“空”值显式转换为 $null 的函数:

function Get-NullIfEmpty {
   param(
       [Parameter(ValueFromPipeline=$true)][array] $CollectionOrEmtpy
   )

   begin { $output = $null }

   process
   {
      if($output -eq $null -and $CollectionOrEmtpy -ne $null){
          $output = @()
      }
      foreach ($element in $CollectionOrEmtpy)
      {
          $output += $element
      }
   }

   end { return $output }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,该方法将如下所示:

[object] WhereObject() {
   return @(1,2) | Where-Object {$_ -gt 2} | Get-NullIfEmpty
}
Run Code Online (Sandbox Code Playgroud)

我试图从类方法中返回一个空数组,但这也很棘手,因为对于常规函数,空数组也意味着“无”。如果您有像 method1 -> function -> method2 - method1 这样的调用链,则会抛出相同的异常。因为该函数将空数组转换为空数组。

所以在我的情况下转换为 $null 是最佳的:)

mkl*_*nt0 7

  • 的(PowerShell的V4 +).Where() 方法,这是在计算表达模式总是返回的一个实例[System.Collections.ObjectModel.Collection[psobject]]

    • 如果没有输入对象匹配,则该实例只是空的(它没有元素并且其.Count属性返回0)。
  • 相比之下,Where-Object cmdlet使用管道语义,这意味着以下输出行为:

    • 如果没有输出(如果没有与过滤器脚本块匹配),则返回值是“空集合”,这在技术上是[System.Management.Automation.Internal.AutomationNull]::Value单例。

    • 如果单个项目匹配,则该项目按原样输出。

    • 如果多个项目匹配并且它们被收集在一个变量中/作为表达式的一部分进行评估,它们将被收集在一个[object[]]数组中。


至于具体的症状——布鲁斯·佩耶特的回答已经确认是一个错误

  • 更新:该错误至少从 v7 开始修复;返回 "nothing" ( AutomationNull) 现在被强制为$null; 请参阅GitHub 上的原始错误报告

内部[List[object]]实例用于收集方法调用的输出,通过内部管道执行。如果该内部管道输出“无” - 即[System.Management.Automation.Internal.AutomationNull]::Value-没有对象添加到列表中。但是,后续代码假设列表中至少有一个对象并盲目访问 index 0,从而导致手头的错误。

问题的更简单再现:

class A {
  # Try to return [System.Management.Automation.Internal.AutomationNull]::Value
  # (which is what `& {}` produces).
  [object] WhereObject(){ return & {} }
}

$a = new-object A

$a.WhereObject() # Throw exception Index was out of range. Must be non-negative and less than the size of the collection.
Run Code Online (Sandbox Code Playgroud)

至于理想的行为

$null如果该方法的代码使用 C# 的默认值功能返回“空集合”,该修复似乎将导致获取输出- 请参阅此注释


Bru*_*tte 6

.Where()运营商总是返回Collection<PSObject>。然而,管道案例不返回任何内容。这是一个问题,因为调用脚本块的代码期望结果中有一个对象,Listresult.Count == 1. 管道案例中没有对象,因此您会收到索引超出范围的错误。所以这是一个错误。我们仍然应该生成一个错误,但它应该是“非空方法必须返回一个值”或诸如此类。顺便说一句 - 有问题代码在这里。