为什么只有一个空数组的数组长度为0?

  • 本文关键字:数组 有一个 arrays powershell
  • 更新时间 :
  • 英文 :


Length属性在我测试的所有数组中都能正常工作,除了一个奇怪的情况:

PS> @(@()).Length
0

并不是说空数组通常会被省略:

PS> @(@(), @()).Length
2
PS> @(@(), @(), @()).Length
3

怎么回事?

  • @(...),数组子表达式操作符不是数组构造函数,它是数组"保证符">(参见下一节),并且嵌套@(...)操作是没有意义的.

    • @(@())实际上与@()相同,即[object[]]类型的空数组。
  • 要无条件构造数组,请使用,,数组构造函数操作符。

    • 要为单个对象构造数组包装器,请使用,一元形式,正如Abraham Zinala所建议的:

      # Create a single-element array whose only element is an empty array.
      # Note: The outer enclosure in (...) is only needed in order to 
      #       access the array's .Count property.
      (, @()).Count # -> 1
      

请注意,我使用了.Count而不是上面的.Length,这更符合powershell的习惯用法;.Count适用于不同的集合类型。尽管System.Array不直接实现.Count,但它通过ICollection接口实现,并且PowerShell允许访问接口成员而不需要强制转换。


背景信息:

  • @(...)主要目的是确保从基于管道命令(例如,@(Get-ChildItem *.txt))收集的输出对象总是作为数组收集。(总是[object[]]类型)-即使...只产生一个输出对象。

    • 如果需要获取数组,则必须使用@(...),因为收集碰巧只包含一个对象的输出将默认原样收集,即不是包装在数组中(这也适用于使用$(...),子表达式操作符);只有对于多个输出对象才使用数组,该数组始终是[object[]]类型的。

    • 注意PowerShell命令(通常)不输出集合;相反,它们一个接一个的(通常是开放式的)对象流到管道中;因此,捕获命令输出需要收集流对象—更多信息请参见此回答。

  • @(...)次要目的是方便定义数组字量,例如@('foo', 'bar')

    • 注意:

      • 使用@(...)来实现这个目的在最初的设计中不是,但是这种使用变得如此普遍,以至于在PowerShell的第5版中实现了一个优化,例如,1, 2——它足以声明一个2元素数组——也可以表示为@(1, 2),而没有不必要的处理开销。

      • 有利的一面是,@(...)总体上在视觉上很独特,在语法上也很方便,特别是用于声明(@())或单元素数组(例如@(42))—如果没有@(...),它们将不得不分别表示为[object[]]:new(), 42

      • 然而,@(...)的这种用法会引起误解,认为它是一个无条件的数组构造函数,但事实并非如此;简而言之:@(...)操作周围包装额外的@(...)操作不会创建嵌套数组,它是一个昂贵的无操作;例如:

        @(42)    # Single-element array
        @(@(42)) # !! SAME - the outer @(...) has no effect.
        
    • @(...)应用于(非数组文字)表达式时,该表达式的计算结果是发送到管道的,该使PowerShell枚举[1]也就是说,如果表达式结果是一个集合,它的元素被一个接一个地发送到管道,类似于命令的流输出,然后再被收集到[object[]]数组.

      # @(...) causes the [int[]]-typed array to be *enumerated*,
      # and its elements are then *collected again*, in an [object[]] array.
      $intArray = [int[]] (1, 2)
      @($intArray).GetType().FullName # -> !! 'System.Object[]'
      
      • 防止此枚举和重新收集:

        • 按原样使用表达式,必要时将其括在(...)

        • 要再次确保返回数组@(...)的有效替代方法是使用[array]强制转换;唯一需要注意的是,如果表达式求值为$null,那么结果也将是$null($null -eq [array] $null):

          # With an array as input, an [array] cast preserves it as-is.
          $intArray = [int[]] (1, 2)
          ([array] $intArray).GetType().FullName # -> 'System.Int32[]'
          # With a scalar as input, a single-element [object[]] array is created.
          ([array] 42).GetType().FullName # -> 'System.Object[]'
          

[1]请参阅本回答的底部部分,了解PowerShell认为管道中可枚举的。net类型。

相关内容