如何正确使用 List 的 ForEach() 语句?

How do I properly use ForEach() statement of List?

我对 List 的 ForEach 方法语法做错了什么感到困惑?

PS D:\ntt> $nicInfo.IpConfigurations.Count
2
PS D:\ntt> $nicInfo.IpConfigurations[0]

PrivateIpAddressVersion Name      Primary PrivateIpAddress PrivateIpAllocationMethod Subnet Name PublicIpAddress Name ProvisioningState
----------------------- ----      ------- ---------------- ------------------------- ----------- -------------------- -----------------
IPv4                    ipconfig1 True    10.233.0.4       Dynamic                                                    Succeeded


PS D:\ntt> $nicInfo.IpConfigurations.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     List`1                                   System.Object


PS D:\ntt> $nicInfo.IpConfigurations.ForEach({$_})
PS D:\ntt>

您是否正在尝试对 collection 中的每一项进行操作?你想做这样的事情吗:

$nicInfo.IpConfigurations | ForEach-Object {
  $ipConfiguration = $_
  write-Output $ipConfiguration
  # do more stuff with this $ipConfiguration
}

仅供参考,您可以将此代码用于 list.ForEach()。

$nicInfo.IpConfigurations.ForEach({write-host $args[0].ToString()})

而且我自己测试过,有效。示例代码如下:

$s=New-Object System.Collections.Generic.List[string]
$s.Add("hello_1")
$s.Add("hello_2")
$s.Add("hello_3")
$s.ForEach({write-host $args[0].ToString()})

测试结果如下:

我也发现了类似的 issue,@PetSerAl 在那里解释得很好。

问题PowerShell's own .ForEach() collection method is preempted by the List<T> type's own .ForEach() method在这种情况下:

  • PowerShell自带的.ForEach({ ... }):

    • $_ 定义为脚本块参数 ({ ... })
    • 手头的输入对象
    • 传递脚本块内产生的任何输出(到 PowerShell 的成功输出流)。
  • 相比之下,List<T>.ForEach({ ... }) 将脚本块转换为 Action<T> 委托,这具有以下含义:

    • 委托不知道脚本块内的 $_,而是收到一个必须作为 [=21] 访问的 参数 =].

    • 来自脚本块的
    • Outputignored,因为根据定义 Action<T> 委托有 无return值.

      • 虽然您可以在脚本块中使用 Write-Host 生成 host(控制台)输出,但无法以编程方式使用此类输出,因为它绕过了 PowerShell 的输出流,因此既不能被捕获也不能被重定向。

感谢 PetSerAl 在评论中提供的重要指示。

解决方法

  • 如果您传递给 .ForEach() 的脚本块不需要产生任何输出,所需要的只是在中使用 $args[0]在您的脚本块中代替 $_,但您仍然可以选择使用下面的其他解决方法之一以避免混淆。

  • 如果需要输出 ,最简单的解决方案是将 List<T> 实例转换为 array with .ToArray() first, .ForEach() 按预期工作;一个简化的例子:

    $list = [System.Collections.Generic.List[object]] ('foo', 'bar')
    $list.ToArray().ForEach({ $_ + '!' }) # Note the .ToArray() call.
    

    以上结果如预期的那样 'foo!', 'bar!'

    • 或者,您可以使用:

      • a foreach 循环来处理列表项,这意味着您必须选择一个迭代变量名称并在循环体中引用它而不是 $_;例如:
        foreach ($itm in $list) { $itm + '!' }
      • ForEach-Object in a pipeline(速度较慢,但​​不需要更改脚本块),如所示;例如:
        $list | ForEach-Object { $_ + '!' }