【问题标题】:How do I properly use ForEach() statement of List?如何正确使用 List 的 ForEach() 语句?
【发布时间】:2018-12-17 22:12:47
【问题描述】:

我很困惑我在 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>

【问题讨论】:

  • 你是如何创建对象的?你确定这个方法存在吗?使用standard foreach loop 怎么样?
  • 这看起来像一个错误! [grin] .Where 方法有效。当集合类型是 Generic.List 时,$_$PSItem 似乎都不适用于 .ForEach()。多么有趣......使用Write-Host 将输出发送到主机,而不是$_ 的东西。使用 Write-Output 根本没有显示任何内容。看起来输出流的处理方式有问题。
  • 您期望发生什么? ForEach 因其副作用而被执行,而您似乎没有任何副作用。 $nicInfo.IpConfigurations.ForEach({param($i) write-host "$i"}) 是做什么的?
  • 我想在foreach语句中做一些数据操作,例子就是例子。
  • @GregorySuvalian List<T> 有自己的 ForEach 方法。因此,它优于 ForEach PowerShell 添加到集合中。

标签: azure powershell azure-powershell


【解决方案1】:

问题PowerShell's own .ForEach() collection method 在这种情况下被the List<T> type's own .ForEach() method抢占

  • PowerShell 自己的.ForEach({ ... })

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

    • 代理不知道脚本块内的$_,而是接收一个必须作为$args[0] 访问的参数

    • 脚本块的

      输出忽略,因为根据定义,Action<T> 委托没有返回值

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

感谢 PetSerAl 提供 cmets 中的关键指针。

解决方法

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

  • 如果需要输出,最简单的解决方案是将List<T> 实例转换为一个数组,首先使用.ToArray(),开启.ForEach() 按预期工作;一个简化的例子:

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

    如预期的那样,上面产生了'foo!', 'bar!'

    • 或者,您可以使用:

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

【讨论】:

  • 感谢@PetSerAl 的黑带解决方法,但我担心它有点太复杂和晦涩难懂。
【解决方案2】:

您是否尝试对集合中的每个项目进行处理?你想做这样的事情吗:

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

【讨论】:

  • ᐩ1 是一种有效的解决方法,尽管它有助于解释为什么需要它。
  • 这不是解释,因为 PowerShell 在 v4+ 中的所有集合(甚至标量)上都提供了 .ForEach() 方法ForEach-Object 解决方法的唯一原因是,在这种情况下,特定集合类型有自己的 .ForEach() 方法,该方法抢占 PowerShell 的。要查看 PowerShell 的 .ForEach() 的实际效果,请尝试使用 (1..3).ForEach({ $_+1 })([collections.arraylist] (1..3)).ForEach({ $_+1 })'foo'.ForEach({ $_+'!' })
【解决方案3】:

仅供参考,您可以将此代码用于 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 在那里解释得很好。

【讨论】:

  • 使用$args[0]而不是$_可以解决输入问题,但是使用Write-Host并不能解决输出问题,因为它直接打印到 host(控制台),这意味着您既不能捕获也不能重定向该输出。
  • @mklement0,感谢您指出,我没有意识到这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-13
  • 2020-09-04
  • 2022-06-13
  • 1970-01-01
  • 2020-09-01
  • 2015-09-15
相关资源
最近更新 更多