Reputation: 2715
I noticed that when running ForEach
on an array object and capturing the output to a new variable, the new variable is not of type 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
Rather, it is of type Collection'1
.
What is this type? Is it equivalent to an array?
BTW, this is not the same as with ForEach-Object
:
PS D:\Playground> $Arr3 = $($Arr | ForEach-Object { $_ })
PS D:\Playground> $Arr3.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
Upvotes: 2
Views: 1155
Reputation: 440536
Let me build on Jeroen Mostert's excellent comment:
The .ForEach()
array method, and its sister method, .Where()
, return [System.Collections.ObjectModel.Collection[psobject]]
collection instances rather than regular PowerShell arrays ([object[]]
).
Unlike the related ForEach-Object
/ Where-Object
cmdlets, these methods always return a collection, even with only a single 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[]
Note: These methods are examples of intrinsic members, i.e. properties and methods PowerShell exposes on all objects, irrespective of their type (unless a type-native member of the same name exists, which takes precedence).
In essence, this collection type behaves like an array in PowerShell (due to implementing the [System.Collections.Generic.IList[psobject]]
interface):
[0]
) is supported, just as with arrays..Add()
) and remove (.Remove()
) elements.[psobject]
(not [object]
), the usually invisible helper type capable of wrapping any .NET object, which PowerShell employs (largely) behind the scenes.
The .ForEach()
method vs. the ForEach-Object
cmdlet:
Note: The following applies analogously to .Where()
vs. Where-Object
.
Use ForEach-Object
on command output, in order to benefit from the streaming behavior of the PowerShell pipeline (one-by-one processing, as input is being received, no need for up-front collection of input); e.g.:
Get-ChildItem -Name *.txt| ForEach-Object { "[$_]" }
Use .ForEach()
on arrays (collections) that are already are / can be collected in memory as a whole first, if faster processing is called for; e.g.:
('foo.txt', 'bar.txt').ForEach({ "[$_]" })
Beware of the differences in single-object behavior and output-collection type discussed above, however.
See this answer for a detailed juxtaposition of .ForEach()
, ForEach-Object
, the foreach
statement, as well as member-access enumeration.
Upvotes: 6