about_Member-Access_Enumeration

简短说明

介绍使用成员访问运算符时的集合自动枚举。

长说明

PowerShell 维护可枚举的类型列表。 从 PowerShell 3.0 开始,成员访问枚举 功能可提高对可枚举的集合对象使用成员访问运算符(.)的便利性。

成员访问枚举可帮助你编写更简单、更短的代码。 可以对集合对象使用成员访问运算符,而不是通过管道将集合对象传递到 ForEach-Object 或使用 ForEach()内部方法访问集合中每个项上的成员。

以下示例生成相同的结果。 最后一个示例演示如何使用成员访问运算符:

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

注意

可以使用成员访问运算符获取集合中项的属性值,但不能使用它来直接设置这些值。 有关详细信息,请参阅 about_Arrays。 成员访问枚举是一项便利功能。 各种枚举方法之间可能存在细微的行为和性能差异。

在对象上使用成员访问运算符并且该对象上存在指定的成员时,将调用该成员。 在没有指定成员的集合对象上使用成员访问运算符时,PowerShell 会枚举该集合中的项,并在每个枚举项上使用成员访问运算符。

在属性的成员访问枚举期间,运算符会返回具有该属性的每个项的属性值。 如果没有项具有指定属性,则运算符返回 $null

在方法的成员访问枚举期间,运算符会尝试对集合中的每个项调用该方法。 如果集合中的任何项没有指定的方法,运算符将 返回 MethodNotFound 异常。

警告

在方法的成员访问枚举期间,会对集合中的每个项调用该方法。 如果要调用的方法发生变化,则会针对集合中的每个项进行更改。 如果在枚举期间发生错误,则仅对出错之前枚举的项调用该方法。 为了提高安全性,请考虑手动枚举项并显式处理任何错误。

访问不可枚举对象的成员

在不是可枚举集合的对象上使用成员访问运算符时,PowerShell 将调用该成员以返回该对象的属性或输出的值。

PS> $MyString = 'abc'
PS> $MyString.Length
3
PS> $MyString.ToUpper()
ABC

对没有成员的不可枚举对象使用成员访问运算符时,PowerShell 将返回缺失属性的 $null 或缺少方法的 MethodNotFound 错误。

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'.

访问集合对象的成员

对具有成员的集合对象使用成员访问运算符时,它始终返回集合对象的属性值或方法结果。

访问集合中存在但集合项中不存在的成员

在此示例中,指定的成员存在于集合中,但不存在于集合的项中。

PS> [System.Collections.Generic.List[string]]$Collection = @('a', 'b')
PS> $Collection.IsReadOnly
False
PS> $Collection.Add('c')
PS> $Collection
a
b
c

访问集合和集合项中存在的成员

在此示例中,指定的成员既存在于集合中,又存在于集合的项中。 将对集合使用成员访问运算符的命令的结果与对 ForEach-Object 中的集合项使用成员访问运算符的结果进行比较。 在集合中,运算符返回集合对象的属性值或方法结果,而不返回其中的项。

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

注意

实现 System.Collections.IDictionary 接口的集合(如 HashTable 和 OrderedDictionary)具有不同的行为。 对键名与属性名相同的字典使用成员访问运算符时,它会返回键的值,而不返回属性的值。

可以使用 psbase 内部成员访问字典对象的属性值。 例如,如果键名为 keys 并且你想要返回 HashTable 键的集合,请使用以下语法

$hashtable.psbase.Keys

访问集合中的所有项上都存在但集合本身中不存在的成员

当对集合对象使用成员访问运算符时,该集合对象没有成员,但包含该成员的项时,PowerShell 会枚举集合中的项,并返回每个项的属性值或方法结果。

PS> [System.Collections.Generic.List[string]]$Collection = @('a', 'b', 'c')
PS> $Collection.Length
1
1
1
PS> $Collection.ToUpper()
A
B
C

访问集合或其元素中不存在的成员

当你对没有成员且其中也没有任何项的集合对象使用成员访问运算符时,如果指定属性,则命令将返回 $null;如果指定方法,则命令将返回 MethodNotFound 错误。

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'.

由于集合对象没有成员,因此 PowerShell 枚举了集合中的项。 请注意,MethodNotFound 错误指定 System.String 不包含该方法,而不是 System.Collections.Generic.List

访问仅存在于集合中的某些项上的方法

当你使用成员访问运算符来访问集合对象的方法时,如果该对象没有这个方法,并且集合中只有部分项有这个方法,那么对于集合中第一个没有该方法的项,命令将返回一个 MethodNotFound 错误。 即使对某些项调用此方法,该命令也仅返回该错误。

PS> @('a', 1, 'c').ToUpper()
InvalidOperation: Method invocation failed because [System.Int32] does not
contain a method named 'ToUpper'.

访问仅存在于集合中某些项的属性

使用成员访问运算符访问没有属性且集合中只有某些项具有该属性的集合对象上的属性时,该命令将返回集合中具有该属性的每个项的属性值。

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

访问嵌套集合的成员

当可枚举集合包含嵌套集合时,成员访问枚举将应用于每个嵌套集合。

例如,$a 是包含两个元素的数组:字符串嵌套数组和单个字符串。

# 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

使用成员访问运算符时,PowerShell 枚举 $a 中的项,并对所有项调用 ToUpper() 方法。

备注

如前所述,各种枚举方法之间可能存在细微的行为和性能差异。

错误导致输出丢失

当成员访问枚举被错误终止时,不会返回先前成功方法调用的输出。 终止错误条件包括:

  • 枚举的对象缺少访问的方法
  • 访问的方法引发终止错误

请考虑以下示例:

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())

$example1 中的两个项都有 Foo() 方法,因此方法调用成功。

PS> $example1.Foo()
Bar
Bar

$example2 中第二项的 Foo() 方法将引发错误,因此枚举失败。

PS> $example2.Foo()
Exception:
Line |
   2 |  class Class2 { [void] Foo() { throw 'Error' } }
     |                                ~~~~~~~~~~~~~
     | Error

$example2 中的第二项没有 Foo() 方法,因此枚举失败。

PS> $example3.Foo()
InvalidOperation: Method invocation failed because [Class3] does not contain
a method named 'Foo'.

将此与使用 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

请注意,输出显示成功调用了数组中第一项上的 Foo()

包含 PSCustomObject 实例的集合

如果对象集合包含 PSCustomObject 项的实例,则在访问的属性缺失时,PowerShell 会意外返回 $null 值。

在以下示例中,至少有一个对象具有引用的属性。

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
]

你希望 PowerShell 返回具有指定属性的项的单个对象。 相反,PowerShell 还会为每个没有该属性的项返回 $null 值。

有关此行为的详细信息,请参阅 PowerShell 问题 #13752

另请参阅