PowerShell Where-Object 与 Where 方法 [英] PowerShell Where-Object vs. Where method
问题描述
我注意到写 PowerShell 类行时发生的一件有趣而奇怪的事情:
An interesting and weird thing I noticed writing PowerShell classes lines:
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
看起来像是设计使然.为什么会这样?
It looks like it is by design. Why does it work so?
解决方法
将空"值显式转换为 $null 的函数:
Function which explicitly convert "empty" value to $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 }
}
在这种情况下,该方法将如下所示:
In this case, the method will look like:
[object] WhereObject() {
return @(1,2) | Where-Object {$_ -gt 2} | Get-NullIfEmpty
}
我试图从类方法返回一个空数组,但这也很棘手,因为对于常规函数,空数组也意味着无".如果您有像 method1 -> function -> method2 - method1 这样的调用链,则会抛出相同的异常.因为函数将空数组转换为空数组.
I tried to return an empty array from the class method, but it is also tricky because for a regular function an empty array means "nothing" as well. If you have a call chain like method1 -> function -> method2 - method1 throw the same exception. Because the function converts an empty array to nothing.
所以在我的情况下转换为 $null 是最佳的 :)
So converting to $null is optimal in my case :)
推荐答案
(PowerShell v4+)
.Where()
方法,在表达式模式、总是返回一个[System.Collections.ObjectModel.Collection[psobject]]
的实例:The (PowerShell v4+)
.Where()
method, which is evaluated in expression mode, always returns an instance of[System.Collections.ObjectModel.Collection[psobject]]
:- 如果没有匹配的输入对象,则该实例只是空(它没有元素并且它的
.Count
属性返回0
).
- If no input objects match, that instance is simply empty (it has no elements and its
.Count
property returns0
).
相比之下,
Where-Object
cmdlet 使用管道语义,这意味着以下内容输出行为:By contrast, the
Where-Object
cmdlet uses pipeline semantics, which implies the following output behavior:如果输出 nothing(如果没有与过滤器脚本块匹配的内容),则返回值是空集合",这在技术上是
[System.Management.Automation.Internal.AutomationNull]::Value
单例.
If nothing is output (if nothing matches the filter script block), the return value is a "null collection", which is technically the
[System.Management.Automation.Internal.AutomationNull]::Value
singleton.
如果单个项目匹配,则该项目按原样输出.
If a single item matches, that item is output as-is.
如果多个项匹配并且它们被收集在一个变量中/作为表达式的一部分进行评估,它们被收集在一个
[object[]]
数组中.If multiple items match and they are collected in a variable / evaluated as part of an expression, they are collected in an
[object[]]
array.至于具体症状 - 其中 Bruce Payette 的回答 已经确认是一个错误.
As for the specific symptom - which Bruce Payette's answer has since confirmed to be a bug.
- 更新:该错误至少从 v7 开始修复;返回无"(
AutomationNull
) 现在被强制为$null
;查看原始 GitHub 上的错误报告.
- Update: The bug is fixed since at least v7; returning "nothing" (
AutomationNull
) is now coerced to$null
; see the original bug report on GitHub.
内部
[List[object]]
实例用于收集方法调用的输出,通过内部管道执行.如果该内部管道输出无"- 即,[System.Management.Automation.Internal.AutomationNull]::Value
- 没有 对象被添加到列表中.但是,后续代码假设列表中至少有一个对象,并盲目访问索引0
,导致手头的错误.An internal
[List[object]]
instance is used to collect the method call's output, executed via an internal pipeline. If that internal pipeline outputs "nothing" - i.e.,[System.Management.Automation.Internal.AutomationNull]::Value
- no object is added to the list. However, subsequent code assumes that there is at least one object in the list and blindly accesses index0
, causing the error at hand.问题的更简单再现:
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.
至于理想的行为:
如果方法的代码返回null 集合",此修复似乎将导致
$null
获得输出,使用 C# 的默认值功能 - 参见 此评论.It seems that the fix will result in
$null
getting output if the method's code returns the "null collection", using C#'s default-value feature - see this comment.这篇关于PowerShell Where-Object 与 Where 方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
- 如果没有匹配的输入对象,则该实例只是空(它没有元素并且它的