PowerShell中[Generic.List].ForEach()方法如何引用传入项
用.NET泛型List的.ForEach()方法替换字符串前缀的解决方案
场景说明
现有一个String类型的.NET泛型List:
$log = [System.Collections.Generic.List[String]]::new() $log.Add('?_Label') $log.Add('=_Item')
通过管道+Foreach-Object可以实现前缀替换:
$log | Foreach-Object {$_ -replace '^\?_', 'E_'}
但尝试使用.NET原生List<T>.ForEach()方法时,几种写法均无效:
$log.ForEach({$_ -replace '^\?_', 'E_' }) $log.ForEach({$arg -replace '^\?_', 'E_' }) $log.ForEach({param([String] $item) $item -replace '^\?_', 'E_' })
关键区别:两个ForEach方法
要解决问题,首先要区分两种不同的ForEach:
- PowerShell原生
.ForEach():PowerShell为所有集合添加的扩展方法,会返回处理后的元素集合,支持用$_指代当前元素,和Foreach-Object管道效果一致。 - .NET
List<T>.ForEach():List<T>类的内置方法,仅接受Action<T>委托,无返回值,仅用于遍历执行操作,不会自动返回处理结果。
正确实现方式
方式1:用PowerShell原生.ForEach()(推荐,和管道效果一致)
直接使用PowerShell为集合添加的.ForEach()方法,写法简单且能返回处理后的集合:
# 得到处理后的新集合,原$log不受影响 $processedLog = $log.ForEach({ $_ -replace '^\?_', 'E_' })
方式2:用.NET List<T>.ForEach()修改原集合
如果一定要使用.NET原生方法,需要将脚本块转换为Action<String>委托,并且因为字符串是不可变类型,要通过索引修改原List的元素:
$index = 0 $log.ForEach([Action[String]]{ param([string]$item) # 修改原List对应索引的元素 $log[$index] = $item -replace '^\?_', 'E_' # 索引自增,必须用script:作用域修改外部变量 $script:index++ }) # 执行后原$log已被修改,其中'?_Label'变为'E_Label'
为什么之前的写法无效
- .NET的
List<T>.ForEach()必须接收Action<T>类型的委托,直接传入未转换的脚本块会导致PowerShell无法识别; $arg不是PowerShell脚本块中当前元素的指代符,正确的参数需要通过param定义并配合委托类型;- 即使定义了
param,如果不转换为Action<String>委托,.NET方法无法匹配参数类型,因此执行失败。
内容的提问来源于stack exchange,提问作者Gordon
相关产品推荐
相关产品推荐

