tl;dr个
您观察到的不对称性肯定是不幸的--但不太可能得到解决,以免 destruct 向后兼容性.
Both
<commandOrExpression> | ...
(pipeline) and
foreach ($var in <commandOrExpression>) { ... }
are enumeration contexts:
100,对应于C#中的null
,是一个特殊的something,恰好发生在represent的"标量(单一)无"中.
这与enumerable null形成对比--从技术上讲,enumerable null是[System.Management.Automation.Internal.AutomationNull]::Value
个单例,也就是"Automation Null"--它是当命令生成no output时得到的.
- 它是enumerable的PS特定表示,即nothing in itself和has no elements:它的目的是发出信号"我什么都不表示-我自己不是对象(除非我被迫在expressions中充当对象(作为标量),在这种情况下,我将假装是
$null
),枚举我产生no个元素".
顺便说一句:在PowerShell 7.4.x中,要确定给定的存储值是实际的$null
还是可枚举的NULL(这一区别与实际的does有关),仍然不是一件容易的事:
# Only $true if $someValue contains the enumerable null
$null -eq $someValue -and @($someValue).Count -eq 0
因此,无论是foreach
循环还是管道:
幸运的是,如果enumerable null被提供为输入,则not执行枚举,假定其目的是用信号通知存在nothing to enumerate.
# Capture the output of a command that produces NO output.
$noOutput = Get-Item *NoSuchFile*
# No output, because the loop body is not executed.
foreach ($obj in $noOutput) { 'This does not print.' }
# Ditto.
$noOutput | ForEach-Object { 'This does not print' }
should将$null
处理为itself,因此导致以$null
作为要处理的(非)对象的单个处理迭代:
至于这种不对称的reason:
直到Windows PowerShell的版本2,将生成no个输出的命令的输出赋给variable隐式地将可枚举空(表示缺少输出)转换为$null
;在v3+(包括PowerShell (Core) 7+中)中,幸运的是现在存储可枚举空.
为了使将输出存储在中间变量中相当于direct处理具有foreach
的命令的输出,决定忽略$null
作为输入,从版本3:[2]开始
例如,如果$null
weren't被foreach
语句忽略,则以下命令的实际上等效公式将是等效的not,并且实际上是were NOT equivalent up to v2:
# DIRECT processing of command output.
foreach ($obj in Get-Item *NoSuchFile*) { 'This does not print' }
# INDIRECT processing of command output, via an intermediate variable.
$output = Get-Item *NoSuchFile*
# In v2, $output was $null, not the enumerable null,
# causing the loop body to execute (once, with $obj containing $null)
foreach ($obj in $output) { 'This does not print' }
Presumably,假设如果使用pipeline,则不需要中间变量,在这种情况下不会出现问题.
However, the problem (still) does arise if non-existent variables or non-existent properties of existing objects are referenced, because they evaluate to $null
, and therefore cause $null
to be sent through the pipeline.
Also, passing the enumerable null as an argument to a function or script's untyped parameter causes quiet conversion to $null
.
从上面得出的结论是,如果对向后兼容性的影响最小,则potential solution应如下所示:
Make references to non-existent variables or properties default to the enumerable null rather than $null
, which would implicitly prevent enumeration in both the pipeline and in foreach
statements.
Additionally, preserve the enumerable null as function / script arguments when binding to untyped parameters.
- 在expression个上下文中,可枚举空的行为类似于
$null
,因此类似$null -eq $noSuchVariable
的内容将继续工作.
就像流水线已经做的那样,将进程actual $null
的值设为foreach
,这将消除不对称性.
具体地说,实现IEnumerable
接口或其泛型对应项的类型会自动枚举,但也有几个例外:字符串、字典(实现IDictionary
或其泛型对应项的类型)和System.Xml.XmlNode
实例.此外,System.Data.DataTable
也通过其.Rows
属性被枚举,尽管它本身没有实现IEnumerable
.
有关详情,请参阅this answer.
[2]v3中的这一行为更改是在2012年PowerShell团队博客上宣布的:New V3 Language Features节,"ForEach语句不迭代超过$NULL",