如何正确使用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>
问题是 List<T>
类型自己的.ForEach()
方法 在这种情况下:
-
PowerShell自己的
.ForEach({ ... })
:- 将
$_
定义为脚本块参数({ ... }
)的输入对象 - 将脚本块内部产生的所有输出传递(到PowerShell的成功输出流).
- 将
-
相反,
List<T>
的.ForEach({ ... })
将脚本块转换为Action<T>
委托,这具有以下含义:-
委托人不了解脚本块内的
$_
,而是收到一个必须以$args[0]
访问的单个自变量.
脚本块中的 -
输出 被忽略,因为根据定义,
Action<T>
委托没有返回值.- 虽然您可以在脚本块内使用
Write-Host
生成 host (控制台)输出,但不能以编程方式使用 这样的输出,因为它会绕过PowerShell的输出流,并且因此无法捕获或重定向.
- 虽然您可以在脚本块内使用
-
要提示 PetSerAl 的提示,以提供评论中的关键指针. >
解决方法:
-
如果您传递给
.ForEach()
的脚本块不需要产生任何输出,则所需的只是在脚本块中使用$args[0]
代替$_
您仍可以选择使用以下其他解决方法之一,以避免造成混淆. -
如果需要输出 ,最简单的解决方案是先将
List<T>
实例转换为 array ,首先使用.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
(速度较慢,但不需要更改脚本块),如;例如:
$list | ForEach-Object { $_ + '!' }
- 一个
-
I'm confused what I'm doing wrong in ForEach method syntax of List?
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>
The problem is that PowerShell's own .ForEach()
collection method is preempted by the List<T>
type's own .ForEach()
method in this case:
PowerShell's own
.ForEach({ ... })
:- defines
$_
as the input object at hand for the script-block argument ({ ... }
) - passes any output produced inside the script block through (to PowerShell's success output stream).
- defines
By contrast,
List<T>
's.ForEach({ ... })
converts the script block to anAction<T>
delegate, which has the following implications:The delegate doesn't know about
$_
inside the script block and instead receives a single argument that must be accessed as$args[0]
.Output from the script block is ignored, because an
Action<T>
delegate by definition has no return value.- While you can produce host (console) output with
Write-Host
from within the script block, such output cannot be used programmatically, because it bypasses PowerShell's output streams and can therefore neither be captured nor redirected.
- While you can produce host (console) output with
Tip of the hat to PetSerAl for providing the crucial pointers in comments.
Workarounds:
If the script block you pass to
.ForEach()
need not produce any output, all that's needed is to use$args[0]
in lieu of$_
in your script block, though you may still opt to use one of the other workarounds below in order to avoid confusion.If output is needed, the simplest solution is to convert the
List<T>
instance to an array with.ToArray()
first, on which.ForEach()
works as expected; a simplified example:$list = [System.Collections.Generic.List[object]] ('foo', 'bar') $list.ToArray().ForEach({ $_ + '!' }) # Note the .ToArray() call.
The above produces
'foo!', 'bar!'
, as expected.Alternatively, you may use:
- a
foreach
loop to process the list items, which means you must pick an iteration variable name and refer to that instead of$_
in the loop body; e.g.:
foreach ($itm in $list) { $itm + '!' }
- or
ForEach-Object
in a pipeline (slower, but doesn't require changing the script block), as shown in No Refunds No Returns' answer; e.g.:
$list | ForEach-Object { $_ + '!' }
- a
这篇关于如何正确使用List的ForEach()语句?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!