Reputation: 2072
In official document it's mentioned that Length
is one alias for Count
, however I found one case that they behave differently. Besides, if I replace ForEach
method with ForEach-Object
, the Length
will emit 3. Can anyone explain this?
> (1..3).ForEach({$_}).Length
1
1
1
> (1..3).ForEach({$_}).Count
3
Upvotes: 2
Views: 726
Reputation: 439058
.Length
and .Count
are aliases of one another and are so-called intrinsic members (properties) that PowerShell adds to most objects, in the interest of unified treatment of scalars and collections (see this answer for background).
The exception are objects of types that PowerShell treats as collections, i.e. objects that it automatically enumerates in the pipeline, which includes instances of type [System.Collections.ObjectModel.Collection[psobject]]
that the .ForEach()
array method returns.
PowerShell does not add .Length
and .Count
properties to instances of what it considers collections, because it assumes that they have such properties themselves, type-natively, assumed to be implemented efficiently (rather than PowerShell having to perform the enumeration and counting the elements).
This is a reasonable assumption that typically holds for .Count
, but rarely for .Length
(arrays (System.Array
) being the one notable example where .Length
exists and returns the element count).
Thus, it's generally better to use .Count
rather than .Length
.
When you access .Length
on an instance of a type that PowerShell considers a collection, yet that type has no such type-native property, PowerShell performs member-access enumeration:
That is, it enumerates the collection and returns the elements' .Length
property values.
In your case, since the elements are scalars (single [int]
objects), they do have the intrinsic .Length
property, which in the case of a scalar always returns 1
.
Therefore:
(1..3).ForEach({$_}).Length
is effectively the same as:
(1..3).ForEach({ $_.Length })
The above explains the (1, 1, 1
) output.
By contrast, the [System.Collections.ObjectModel.Collection[psobject]]
does have a type-native .Count
property, which returns the count of elements, as expected.
Background information:
Note:
Only those types that PowerShell does not consider collections get the intrinsic .Count
and .Length
properties.
Conversely, types that are considered collections may or may not have any, either, or both such properties type-natively, and in the absence of a type-native property member-access enumeration is performed instead.
[Linq.Enumerable]::Range(1,3).Count
yields 1, 1, 1
.Which .NET types PowerShell considers collections (enumerables) and therefore automatically enumerates in the pipeline:
Any type that implements the IEnumerable
/ IEnumerable`1
.NET interface(s)
Except the following, which are not automatically enumerated:
IDictionary
/ IDictionary`2
, which notably includes [hashtable]
(@{ ... }
, as a literal) and its ordered cousin, OrderedDictionary
([ordered] @{ ... }
, as a literal)
XmlNode
, the base type for [xml]
(XmlDocument
)
Strings (String
); note that 'foo'.Count
is 1
(the intrinsic property reflecting that a string is considered a scalar (single thing)), whereas 'foo'.Length
is 3
(the type-native property that returns the character count).
Additionally, System.Data.DataTable
(which itself doesn't implement IEnumerable
, only its .Rows
property does).
Upvotes: 8