我注意到编写 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 是最佳的:)
的(PowerShell的V4 +).Where() 方法,这是在计算表达模式,总是返回的一个实例[System.Collections.ObjectModel.Collection[psobject]]:
.Count属性返回0)。相比之下,Where-Object cmdlet使用管道语义,这意味着以下输出行为:
如果没有输出(如果没有与过滤器脚本块匹配),则返回值是“空集合”,这在技术上是[System.Management.Automation.Internal.AutomationNull]::Value单例。
如果单个项目匹配,则该项目按原样输出。
如果多个项目匹配并且它们被收集在一个变量中/作为表达式的一部分进行评估,它们将被收集在一个[object[]]数组中。
至于具体的症状——布鲁斯·佩耶特的回答已经确认是一个错误。
内部[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# 的默认值功能返回“空集合”,该修复似乎将导致获取输出- 请参阅此注释。