To complement Mathias R. Jessen's helpful answer:
tl;dr
Use of the .ForEach()
array method enables a concise and performant solution:
# Return the files' .Length property values as a collection.
(Get-ChildItem -File -Recurse -Path $path).ForEach('Length')
As pointed out in Mathias' answer, type-native properties - such as .Length
on array instances - take precedence over the so-called member-access enumeration that you tried to perform - that is, you wanted to collect the .Length
property values of the elements of your collection (array; the System.IO.FileInfo
instances output by Get-ChildItem
), and return them as an ([object[]]
) array.
- GitHub issue #7445 discusses this situational ambiguity and proposes introducing a dedicated operator, say
%.
so that you can unambiguously request either regular property access or member-access enumeration.
A perhaps surprising aspect of member-access enumeration is that it applies pipeline logic in that it situationally returns a scalar, namely if the collection happens to contain just one element.
- E.g.,
@(Get-Date).Year.GetType().Name
returns an System.Int32
, not Object[]
, indicating that an integer rather than a (single-element) array containing an integer was returned.
- GitHub issue #6802 discusses this.
Member-access enumeration is not only convenient, but also fast, because it avoids looping / enumeration in PowerShell code.
Use of the ForEach-Object
or Select-Object
cmdlets, as shown in Mathias' answer, is definitely a functional but slow workaround for not being able to use member-access enumeration in a given situation, due to use of the pipeline, which isn't necessary for input that that is already in memory in full.
Therefore, use of the .ForEach()
array method - using the overload where you simply specify the target property name ('Length'
) - is a concise and better-performing alternative.
- Note: Unlike with member-access enumeration, the return value from
.ForEach()
is always a collection, albeit not an array: it is of type [System.Collections.ObjectModel.Collection[psobject]]
, which, however, will behave like an array in most contexts.[1]
[1] Unlike an array, this type of collection is extensible (you can add or remove elements). Due to the element type being [psobject]
, each element is typically invisibly wrapped in this type; e.g., 42 -is [psobject]
is $false
, but @(42).ForEach({ $_ })[0] -is [psobject]
is $true
. Unfortunately, there are cases where this near-invisible wrapper results in different behavior - see GitHub issue #5579.