为什么 $null 在 ForEach-Object {} 与 foreach() 中的行为不同?

问题描述 投票:0回答:1

考虑以下示例:

$null

foreach ($n in $null) {'This is a $null test'}
(no output)

$null | ForEach-Object {'This is a $null test'}
This is a $null test

$null -in $null
True

$null -contains $null
True


[int]1

foreach ($n in [int]1) {'Test'}
Test

[int]1 | ForEach-Object {'Test'}
Test

[int]1 -in [int]1
True

[int]1 -contains [int]1
True


由于 $null 是一个不包含任何内容的标量值,因此在第二个 $null 示例中,$null 将沿着管道发送一个“nothing”的单个实例,这解释了输出。

为什么即使

$null
返回
$null -in $null
,也不能将
True
作为集合进行迭代?

  • 迭代评估的空对象/值所带来的性能提升似乎很明显,但为什么 PowerShell 将标量变量/值视为集合?
  • 此行为是否在整个 .NET 和 .NET Framework 中持续存在?
  • 是否发生了类型强制,或者我对集合/列表的结构或行为方式有根本性的误解吗?
.net powershell null scalar
1个回答
0
投票

tl;博士

您观察到的不对称性绝对是不幸的 - 但不太可能得到解决,以免破坏向后兼容性。


两者

<commandOrExpression> | ...
(管道)和
foreach ($var in <commandOrExpression>) { ... }

枚举上下文:

  • 如果

    <commandOrExpression>
    计算结果为 可枚举 的对象,则会自动枚举该对象,并且 枚举的元素将被一一处理。

  • 如果不存在,则结果按

    本身处理;从某种意义上说:它被视为像单元素可枚举


  • $null

    ,对应于 C# 中的 null
    ,是一个特殊的 
    something,恰好 代表“标量(单个)无”。

  • 这与

    可枚举空形成对比 - 从技术上讲,[System.Management.Automation.Internal.AutomationNull]::Value

    单例,又名“自动化空” - 这是当命令产生
    无输出时得到的结果。

      它是
    • 可枚举的 PS 特定表示,它本身什么都没有并且也没有元素:它的目的是发出信号“我什么也不代表 - 我自己不是一个对象(除非我被迫)在表达式中充当一个(作为标量),在这种情况下我会假装是$null
      ),并且枚举我会产生
      没有元素”。

因此,

foreach

循环和管道:

  • 幸运的是,如果提供

    可枚举 null 作为输入,则 do 执行 no 枚举,因为它的目的是表明没有 没有可枚举的

    # 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' }
    
    
  • 应该$null

    作为
    本身进行处理,因此会导致以$null
    作为要处理的(非)对象的单次迭代:

    • 幸运的是,这

      它在管道中的工作方式,正如您问题中对ForEach-Object

      cmdlet的示例调用所证明的那样。
      

    • 不幸的是,这不是foreach

      语言语句的行为方式,出人意料的是处理$null,正如您也观察到的那样。
      
      

至于这种不对称的
可能原因

    在 Windows PowerShell 版本 2 之前,将生成
  • no

    输出的命令的输出分配给 variable 会隐式地将可枚举 null(表示缺少输出)转换为 $null;在 v3+ 中(包括在

    PowerShell (Core) 7+ 中),幸运的是,可枚举 null 现在已这样存储。
    
    

  • 大概

    试图将输出存储在中间变量中相当于使用foreach直接处理命令的输出,因此决定忽略$null

    作为输入。

    例如,如果
      $null
    • weren't

      foreach 语句忽略,则以下命令的等效表述将not
       等效:
      # 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 is now $null, not the enumerable null, # and if $null were enumerated, the loop body would execute. foreach ($obj in $output) { 'This does not print' }

      
      
  • 大概
  • ,假设如果使用

    pipeline,则不需要中间变量,在这种情况下不会出现问题。

    但是,如果引用现有对象的
  • 不存在的变量
  • 不存在的属性,就会出现问题,因为它们的计算结果为$null,因此导致$null通过管道发送.

    
    
    
    

    从上面可以看出,一个
  • 潜在的解决方案
应该对向后兼容性影响最小(如果有的话)如下:

将对不存在的变量或属性的引用默认为

enumerable null
    而不是
  • $null

    ,这将隐式阻止管道和 foreach 语句中的枚举。

    
    
    expression

    上下文中,可枚举 null 的行为类似于
      $null
    • ,因此类似 $null -eq $noSuchVariable 的内容将继续起作用。
      
      
    • 使
    foreach
  • 也处理
  • 实际

    $null
    值,就像管道已经做的那样,这将消除不对称性。
        

© www.soinside.com 2019 - 2024. All rights reserved.