Powershell数组的ForEach不返回System.array?

Yoa*_*ein 2 powershell

我注意到,当ForEach在数组对象上运行并将输出捕获到新变量时,新变量不是 System.array 类型:

PS D:\Playground> $Arr = 1, 2, 3
PS D:\Playground> $Arr2 = $Arr.ForEach({$_})
PS D:\Playground> $Arr2.Gettype()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Collection`1                             System.Object

Run Code Online (Sandbox Code Playgroud)

相反,它是 类型Collection'1

这是什么类型?它相当于数组吗?

顺便说一句,这与以下内容不同ForEach-Object

PS D:\Playground> $Arr3 = $($Arr | ForEach-Object { $_ })
PS D:\Playground> $Arr3.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array
Run Code Online (Sandbox Code Playgroud)

mkl*_*nt0 5

让我以Jeroen Mostert的精彩评论为基础:

  • 数组.ForEach()方法及其姐妹方法.Where()返回[System.Collections.ObjectModel.Collection[psobject]]集合实例而不是常规 PowerShell 数组 ( [object[]])。

    • 与相关的ForEach-Object/ cmdlet不同,这些方法始终返回一个集合,即使只有一个对象Where-Object

      # .ForEach() method:
      # Collection result even with single object.
      @(1).ForEach({ $_ }).GetType().Name # -> Collection`1
      
      # ForEach-Object cmdlet:
      # Single output object: received as-is.
      (@(1) | ForEach { $_ }).GetType().Name # -> Int32
      # Two or more output objects: array result (if captured / used in expression)
      (1, 2 | ForEach { $_ }).GetType().Name # -> Object[]
      
      Run Code Online (Sandbox Code Playgroud)
    • 注意:这些方法是内部成员的示例,即PowerShell 在所有对象上公开的属性和方法,无论其类型如何除非存在同名的类型本机成员,该成员优先)。

  • 本质上,此集合类型的行为类似于PowerShell 中的数组(由于实现了[System.Collections.Generic.IList[psobject]]接口):

    • 它的元素在管道中枚举,就像数组的元素一样。
    • [0]与数组一样,支持位置索引(例如)。
    • 然而,与数组不同的是
      • 它是可调整大小的;也就是说,它的实例允许您添加 ( .Add()) 和删除 ( .Remove()) 元素。
      • 它的元素类型是[psobject](not [object]),通常是不可见的帮助器类型,能够包装任何 .NET 对象,PowerShell(大部分)在幕后使用它。
        • 通常,这种差异并不重要,但不幸的是,在某些边缘情况下,这种差异会很重要,请参阅GitHub 问题 #5579

.ForEach() 方法cmdletForEach-Object 对比:

注意:以下内容类似地适用于.Where()vs. Where-Object

  • ForEach-Object命令输出上使用,以便从PowerShell 管道的流式行为中受益(一对一处理,在接收输入时,无需预先收集输入);例如:

    Get-ChildItem -Name *.txt| ForEach-Object { "[$_]" }
    
    Run Code Online (Sandbox Code Playgroud)
  • 如果需要更快的处理,请首先用于已经/可以作为一个整体收集在内存中的.ForEach()数组(集合) ;例如:

    ('foo.txt', 'bar.txt').ForEach({ "[$_]" })
    
    Run Code Online (Sandbox Code Playgroud)

但是,请注意上面讨论的单对象行为和输出集合类型的差异。

有关、、语句以及成员访问枚举的详细并置,请参阅此答案.ForEach()ForEach-Objectforeach

  • @Santiago,我不知道像 `(1, 2).foreach({ begin { 0 } process { "[$_]" } end { 3 } })` 这样的东西有效 - 谢谢!请注意,对于流(内存限制)处理,您仍然需要“ForEach-Object”。此外,“ForEach-Object”和“Where-Object”如果实现得高效的话,执行速度的问题可能会少得多 - 请参阅 [GitHub 功能请求 #10982](https://github.com/PowerShell/PowerShell/问题/10982)。 (2认同)