【问题标题】:ArrayList .Add vs .AddRange vis-a-vis the PipelineArrayList .Add 与 .AddRange 相对于管道
【发布时间】:2020-08-16 17:17:54
【问题描述】:

给定一个正确定义的变量

$test = New-Object System.Collections.ArrayList

.Add 用数组中的项目数污染管道,而.AddRange 不会。 $test.Add('Single') 会将计数转储到控制台。 $test.AddRange(@('Single2')) 将毫不费力地清理干净。为什么会有不同的行为?这只是一个疏忽,还是有一些我不理解的故意行为?

鉴于.AddRange 需要在不使用变量时强制转换为数组(这已经是一个数组),当我知道我只需要添加一项时,我倾向于使用[void]$variable.Add('String'),当我知道我只需要添加一项时使用[void]$test.AddRange($variable)我正在向数组中添加一个数组,即使 $variable 仅包含或只能包含单个项目。 [void] 这里不是必需的,但我想知道拥有它是否只是最佳实践,当然取决于上面的答案。还是我也错过了什么?

【问题讨论】:

  • 这能回答你的问题吗? Return Multidimensional Array From Function: $test.Add(,@('Single'))
  • "添加污染管道",不完全正确,你在输出的时候把项目放在管道上(默认:Write-Output ) 对象 ($Test)。
  • 我想我的问题与为什么不同?为什么.Add 将计数转储到管道而.AddRange 没有?如果我想知道添加一项后得到的数组有多大,那么在添加 N 项后我是否也想知道(甚至更多)?真的,我什么时候想知道管道?我总是可以得到.Count。至于这与展开行为有什么关系,我看不出有什么联系,但对我来说现在也是早上,我还在喝第一杯咖啡,所以我可能会再看一下,看看是否有什么开始有意义.
  • 而且,关于污染,我从来没有真正输出过$test。当我这样做时,我期望$test 的内容。但是$variable.Add('String') 不是输出,它只是在修改,在控制台返回1 对我来说似乎是污染。
  • 这整个讨论可以避免,因为 arralylist 类型已被弃用 - 并且已经被弃用了好几年。 [grin] 所以......不要使用它。相反,使用类似generic.list 类型的东西。不会发出添加项目的索引。另外,它的速度非常快。

标签: powershell arraylist pipeline


【解决方案1】:

为什么会有不同的行为?这只是一个疏忽,还是有一些我不理解的故意行为?

因为很多年前,有人决定 ArrayList 应该这样做!

Add() 返回参数插入列表的索引,这可能确实有用且有意义。

另一方面,对于AddRange(),目前还不清楚为什么它应该返回任何东西,如果是,什么?输入参数中 first 项的索引? 最后一个?还是应该返回一个包含所有插入索引的可变大小数组?那会很尴尬!所以实现ArrayList的人决定不返回任何东西。

C#VB.NET 中,ArrayList 最初是为此设计的,“污染管道”实际上并不作为一个概念存在,如果有人,运行时将简单地省略将返回值复制回调用者调用 .Add() 而不分配给变量。

这里的[void] 不是必需的,但我想知道拥有它是否只是最佳实践,当然这取决于上面的答案。还是我也错过了什么?

不,完全没有必要。 AddRange() 不是神奇地有一天会改变输出任何东西。


如果您不需要知道插入索引,请改用[System.Collections.Generic.List[psobject]]

$list = [System.Collections.Generic.List[psobject]]::new()

# this won't return anything, no need for `[void]`
$list.Add(123)

如果出于某种原因您必须使用ArrayList,您可以通过覆盖Add() 方法来“静默”它:

function New-SilentArrayList {
  # Create a new ArrayList
  $newList = [System.Collections.ArrayList]::new()

  # Create a new `Add()` method, then return the list
  $newAdd  = @{
    InputObject = $newList
    MemberType = 'ScriptMethod' 
    Name = 'Add'
    Value = {param($obj) $this.AddRange(@($obj))}
  }
  Write-Output $( 
    Add-Member @newAdd -Force -PassThru
  ) -NoEnumerate
}

现在你的ArrayListAdd() 再也不会偷看了!

PS C:\> $list = New-SilentArrayList
PS C:\> $list.Add(123)
PS C:\> $list
123

【讨论】:

    【解决方案2】:

    显然我没听懂你要去哪里。
    添加会污染 管道”,再想想是正确的说法 .Net 方法如$variable.Add('String') 不 单独使用 PowerShell 管道(直到您使用 Write-Output 命令输出数组,如果您不将其分配给变量,这是默认命令)。

    Write-Output cmdlet 通常在脚本中用于显示 控制台上的字符串和其他对象。但是,因为默认 行为是在管道末端显示对象,它是 一般不需要使用 cmdlet。

    重点是ArrayListAdd 方法返回[Int32]添加值的ArrayList 索引”,而AddRange 不返回任何内容。这意味着如果您不将结果分配给其他东西(包括 $Null = $test.Add('Single')),它确实会输出到 PowerShell 管道。
    相反,您也可以考虑使用 List 类的 Add 方法,该方法也不返回任何内容,另请参见:ArrayList vs List<> in C#
    但总的来说,我建议使用确实使用管道的本机 PowerShell 命令
    (我不能给你一个很好的例子,因为不清楚你期望什么输出,但我注意到你删除的另一个问题这个问题,我想这个Why should I avoid using the increase assignment operator (+=) to create a collection 的答案可能会帮助你进一步)

    【讨论】:

    • 我想我仍然想了解的是,为什么.Add() 返回[int32].AddRange() 什么也没返回?如此相似的方法中不同的返回行为的底层逻辑是什么?至于管道,我发现它对于单行程序和简单脚本非常有用,但是当您处理大型程序时,调试可能会很痛苦,而且管道中的结果和最终结果是如此不一致.这是我现在将所有内容重构为类的原因之一,以完全避免管道并从 PowerShell 中获得更具确定性的行为。
    • 关于其他问题,我搞砸了我的测试代码。我试图用foreach ($I in 1.1000) { 而不是foreach ($I in 1..1000) { 重复测量,看起来+= 更快。修改代码后,我看到了预期的结果,+= 更慢,迭代次数越多越慢。
    猜你喜欢
    • 1970-01-01
    • 2016-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-10
    • 1970-01-01
    • 2015-02-11
    • 2011-08-24
    相关资源
    最近更新 更多