PetSerAl就像他经常做的那样,在对该问题的评论中提供了关键指针:
成员枚举——在PSv3中引入了访问集合上的成员(属性或方法)并将其隐式应用于其每个元素的能力,并将结果收集到一个数组中。[1]
成员枚举不仅具有表达性和方便性,而且比其他方法更快。
一个简化的例子:
PS> ((Get-Item /), (Get-Item $HOME)).Mode
d--hs- # The value of (Get-Item /).Mode
d----- # The value of (Get-Item $HOME).Mode
应用于-enclosed 命令输出.Mode
的集合会导致在集合中的每个项目上访问该属性,结果值作为数组(常规 PowerShell 数组,类型为)返回。(...)
.Mode
[System.Object[]]
注意事项:成员枚举像管道一样处理结果数组,这意味着:
如果数组只有一个元素,则直接返回该元素的属性值,而不是在单个元素数组中:
PS> @([pscustomobject] @{foo=1}).foo.GetType().Name
Int32 # 1 was returned as a scalar, not as a single-element array.
如果收集的属性值本身是数组,则返回一个平面数组:
PS> @([pscustomobject] @{foo=1,2}, [pscustomobject] @{foo=3,4}).foo.Count
4 # a single, flat array was returned: 1, 2, 3, 4
此外,成员枚举仅适用于获取(读取)属性值,不适用于设置(写入)它们。
这种不对称是设计使然,以避免潜在的不需要的批量修改;在 PSv4+ 中,.ForEach('<property-name', <new-value>)
用作最快的解决方法(见下文)。
这个方便的功能不可用,但是:
- 如果您在PSv2上运行(绝对)
- 如果集合本身具有指定名称的成员,则在这种情况下应用集合级别的成员。
例如,即使在 PSv3+ 中,以下内容也不会执行成员枚举:
PS> ('abc', 'cdefg').Length # Try to report the string lengths
2 # !! The *array's* .Length property value (item count) is reported, not the items'
在这种情况下——通常在PSv2中——需要一种不同的方法:
- 最快的替代方案,使用
foreach
语句,假设整个集合作为一个整体适合内存(使用成员枚举时暗示)。
PS> foreach ($s in 'abc', 'cdefg') { $s.Length }
3
5
- PSv4+替代方案,使用集合方法
.ForEach()
,也对集合进行整体操作:
PS> ('abc', 'cdefg').ForEach('Length')
3
5
注意:如果适用于输入集合,也可以使用设置属性值.ForEach('<prop-name>', <new-value>)
,这是解决无法使用的最快解决方法.<prop-name> = <new-value>
,即无法使用成员枚举设置属性值。
注意:只有在单独处理项目而不在内存中收集结果的情况下,使用管道才可以节省内存。
使用ForEach-Object
cmdlet,如Burt Harris 的有用回答:
PS> 'abc', 'cdefg' | ForEach-Object { $_.Length }
3
5
仅对于属性(相对于方法),Select-Object -ExpandProperty
是一个选项;它在概念上清晰而简单,并且在ForEach-Object
性能方面几乎与该方法相当(有关性能比较,请参阅此答案的最后一部分):
PS> 'abc', 'cdefg' | Select-Object -ExpandProperty Length
3
5
[1] 在撰写本文时,该功能已被描述,但未在概念帮助主题中命名。GitHub 文档问题 #8437要求为其提供正式名称。about_Properties