about_Member-Access_Enumeration
Short description
Describes the automatic enumeration of collections when using the member-access operator.
Long description
PowerShell maintains a list of types that are enumerable. Starting in
PowerShell 3.0, the member-access enumeration feature improves the
convenience of using the member-access operator (.
) on collection objects
that are enumerable.
Member-access enumeration helps you write simpler and shorter code. Instead of
piping a collection object to ForEach-Object
or using the ForEach()
intrinsic method to access members on each item in the collection, you
can use the member-access operator on the collection object.
The following examples produce the same results. The last example demonstrates the use of the member-access operator:
PS> Get-Service -Name event* | ForEach-Object -Process { $_.DisplayName }
Windows Event Log
COM+ Event System
PS> (Get-Service -Name event*).ForEach({ $_.DisplayName })
Windows Event Log
COM+ Event System
PS> (Get-Service -Name event*).DisplayName
Windows Event Log
COM+ Event System
Note
You can use the member-access operator to get the values of a property on items in a collection but you can't use it to set them directly. For more information, see about_Arrays. Member-access enumeration is a convenience feature. There can be subtle behavior and performance differences between the various enumeration methods.
When you use the member-access operator on an object and the specified member exists on that object, the member is invoked. When you use the member-access operator on a collection object that doesn't have the specified member, PowerShell enumerates the items in that collection and uses the member-access operator on each enumerated item.
During member-access enumeration for a property, the operator returns the value
of the property for each item that has that property. If no items have the
specified property, the operator returns $null
.
During member-access enumeration for a method, the operator attempts to call the method on each item in the collection. If any item in the collection does not have the specified method, the operator returns the MethodNotFound exception.
Warning
During member-access enumeration for a method, the method is called on each item in the collection. If the method you are calling makes changes, the changes are made for every item in the collection. If an error occurs during enumeration, the method is called only on the items enumerated before the error. For additional safety, consider manually enumerating the items and explicitly handling any errors.
Access members of a non-enumerable object
When you use the member-access operator on an object that isn't an enumerable collection, PowerShell invokes the member to returns the value of the property or output of the method for that object.
PS> $MyString = 'abc'
PS> $MyString.Length
3
PS> $MyString.ToUpper()
ABC
When you use the member-access operator on a non-enumerable object that doesn't
have the member, PowerShell returns $null
for the missing property or a
MethodNotFound error for the missing method.
PS> $MyString = 'abc'
PS> $null -eq $MyString.DoesNotExist
True
PS> $MyString.DoesNotExist()
InvalidOperation: Method invocation failed because [System.String] does not contain a method named 'DoesNotExist'.
Access members of a collection object
When you use the member-access operator on a collection object that has the member, it always returns the property value or method result for the collection object.
Access members that exist on the collection but not its items
In this example, the specified members exist on the collection but not the items in it.
PS> [System.Collections.Generic.List[string]]$Collection = @('a', 'b')
PS> $Collection.IsReadOnly
False
PS> $Collection.Add('c')
PS> $Collection
a
b
c
Access members that exist on the collection and its items
For this example, the specified members exist on both the collection and the
items in it. Compare the results of the commands using the member-access
operator on the collection to the results from using the member-access operator
on the collection items in ForEach-Object
. On the collection, the operator
returns the property value or method result for the collection object and not
the items in it.
PS> [System.Collections.Generic.List[string]]$Collection = @('a', 'b', 'c')
PS> $Collection.Count
3
PS> $Collection | ForEach-Object -Process { $_.Count }
1
1
1
PS> $Collection.ToString()
System.Collections.Generic.List`1[System.String]
PS> $Collection | ForEach-Object -Process { $_.ToString() }
a
b
c
Note
Collections that implement the System.Collections.IDictionary interface, such as HashTable and OrderedDictionary, have a different behavior. When you use the member-access operator on a dictionary that has a key with the same name as a property, it returns the key's value instead of the property's.
You can access the dictionary object's property value with the psbase
intrinsic member. For example, if the key name is keys
and you want
to return the collection of the HashTable keys, use this syntax:
$hashtable.psbase.Keys
Access members that exist on all items in a collection but not itself
When you use the member-access operator on a collection object that doesn't have the member but the items in it do, PowerShell enumerates the items in the collection and returns the property value or method result for each item.
PS> [System.Collections.Generic.List[string]]$Collection = @('a', 'b', 'c')
PS> $Collection.Length
1
1
1
PS> $Collection.ToUpper()
A
B
C
Access members that don't exist on collection or its items
When you use the member-access operator on a collection object that doesn't
have the member and neither do the items in it, the command returns $null
if
you specify a property or a MethodNotFound
error if you specify a method.
PS> [System.Collections.Generic.List[string]]$Collection = @('a', 'b', 'c')
PS> $null -eq $Collection.DoesNotExist
True
PS> $Collection.DoesNotExist()
InvalidOperation: Method invocation failed because [System.String] does not
contain a method named 'DoesNotExist'.
Because the collection object doesn't have the member, PowerShell enumerated the items in the collection. Notice that the MethodNotFound error specifies that System.String doesn't contain the method instead of System.Collections.Generic.List.
Access methods that exist only on some items in a collection
When you use the member-access operator to access a method on a collection
object that doesn't have the method and only some items in the collection have
it, the command returns a MethodNotFound
error for the first item in the
collection that doesn't have the method. Even though the method gets called on
some items, the command only returns the error.
PS> @('a', 1, 'c').ToUpper()
InvalidOperation: Method invocation failed because [System.Int32] does not
contain a method named 'ToUpper'.
Access properties that exist only on some items in a collection
When you use the member-access operator to access a property on a collection object that doesn't have the property and only some items in the collection have it, the command returns the property value for each item in the collection that has the property.
PS> $CapitalizedProperty = @{
MemberType = 'ScriptProperty'
Name = 'Capitalized'
Value = { $this.ToUpper() }
PassThru = $true
}
PS> [System.Collections.Generic.List[object]]$MixedCollection = @(
'a'
('b' | Add-Member @CapitalizedProperty)
('c' | Add-Member @CapitalizedProperty)
'd'
)
PS> $MixedCollection.Capitalized
B
C
Access members of a nested collection
When an enumerable collection contains a nested collection, member-access enumeration is applied to each nested collection.
For example, $a
is an array containing two elements: a nested array of
strings and a single string.
# Get the count of items in the array.
PS> $a.Count
2
# Get the count of items in each nested item.
PS> $a.GetEnumerator().Count
2
1
# Call the ToUpper() method on all items in the nested array.
PS> $a = @(, ('bar', 'baz'), 'foo')
PS> $a.ToUpper()
BAR
BAZ
FOO
When you use the member-access operator, PowerShell enumerates the items in
$a
and calls the ToUpper()
method on all items.
Notes
As previously stated, there can be subtle behavior and performance differences between the various enumeration methods.
Errors result in lost output
When member-access enumeration is terminated by an error, output from prior successful method calls isn't returned. Terminating error conditions include:
- the enumerated object lacks the accessed method
- the accessed method raises a terminating error
Consider the following example:
class Class1 { [object] Foo() { return 'Bar' } }
class Class2 { [void] Foo() { throw 'Error' } }
class Class3 {}
$example1 = ([Class1]::new(), [Class1]::new())
$example2 = ([Class1]::new(), [Class2]::new())
$example3 = ([Class1]::new(), [Class3]::new())
Both items in $example1
have the Foo()
method, so the method call succeeds.
PS> $example1.Foo()
Bar
Bar
The Foo()
method on second item in $example2
throws an error, so the
enumeration fails.
PS> $example2.Foo()
Exception:
Line |
2 | class Class2 { [void] Foo() { throw 'Error' } }
| ~~~~~~~~~~~~~
| Error
The second item in $example2
doesn't have the Foo()
method, so the
enumeration fails.
PS> $example3.Foo()
InvalidOperation: Method invocation failed because [Class3] does not contain
a method named 'Foo'.
Compare this to enumeration using ForEach-Object
PS> $example2 | ForEach-Object -MemberName Foo
Bar
ForEach-Object: Exception calling "Foo" with "0" argument(s): "Error"
PS> $example3 | ForEach-Object -MemberName Foo
Bar
Notice that the output show the successful call to Foo()
on the first item
in the array.
Collections containing PSCustomObject instances
If the collection of objects contains instances of PSCustomObject items,
PowerShell unexpectedly retruns $null
values when the accessed property is
missing.
In the following examples at least one object has the referenced property.
PS> $foo = [pscustomobject]@{ Foo = 'Foo' }
PS> $bar = [pscustomobject]@{ Bar = 'Bar' }
PS> $baz = [pscustomobject]@{ Baz = 'Baz' }
PS> ConvertTo-Json ($foo, $bar, $baz).Foo
[
"Foo",
null,
null
]
PS> ConvertTo-Json ((Get-Process -Id $PID), $foo).Name
[
"pwsh",
null
]
You would expect PowerShell to return a single object for the item that has the
property specified. Instead, PowerShell also returns a $null
value for each
item that doesn't have the property.
For more information on this behavior, see PowerShell Issue #13752.