从函数返回 PSObjects 的 ArrayList 时的空白记录

Blank records when returning ArrayList of PSObjects from function

所以我正在重构一个 Powershell 脚本并将很多东西移到函数中。当我 return 一个包含 42 PS 个对象的 ArrayList 时,来自函数(称为 Get-OutList)的 return $out,42 条空白记录被插入到 ArrayList 的开头,然后我的原始记录关注

我的函数如下所示:

function Get-OutList {
    param (
        [Parameter(Position=0,mandatory=$true)]
        [PSObject]$RoleCollection,
        [Parameter(Position=1,mandatory=$true)]
        [PSObject]$MemberCollection
    )

    $out = New-Object System.Collections.ArrayList
    $MemberCollection.result | ForEach-Object {
        $currentMember = $_
        $memberDetail = New-Object PSObject
        Add-Member -InputObject $memberDetail -MemberType NoteProperty -Name FirstName -Value $($currentMember.user.first_name)
        Add-Member -InputObject $memberDetail -MemberType NoteProperty -Name LastName -Value $($currentMember.user.last_name)
        Add-Member -InputObject $memberDetail -MemberType NoteProperty -Name Email -Value $($currentMember.user.email)
        Add-Member -InputObject $memberDetail -MemberType NoteProperty -Name Status -Value $($currentMember.status)
        
        $RoleCollection.result | ForEach-Object {
            Add-Member -InputObject $memberDetail -MemberType NoteProperty -Name $_.name -Value (&{If($currentMember.roles.name -contains $_.name){"Y"}Else{""}})
        }
        $out.Add($memberDetail)
    }
    return $out
}

我了解 Powershell 将每条记录枚举回调用函数的位置,但我尝试了一些方法都无济于事:

为什么我不能让我的对象与从函数 returned 之前一样?如有任何帮助,我们将不胜感激!

编辑#1 包括一个易于重现的示例:

function Get-OutList {
    param (
        [Parameter(Position=0,mandatory=$true)]
        [PSObject]$MemberCollection
    )

    $out = New-Object 'System.Collections.ArrayList'
    $MemberCollection | ForEach-Object {
        $memberDetail = New-Object PSObject
        Add-Member -InputObject $memberDetail -MemberType NoteProperty -Name FirstName -Value "One"
        Add-Member -InputObject $memberDetail -MemberType NoteProperty -Name LastName -Value "Two"
        Add-Member -InputObject $memberDetail -MemberType NoteProperty -Name Email -Value "Three"
        Add-Member -InputObject $memberDetail -MemberType NoteProperty -Name Status -Value "Four"
        $out.Add($memberDetail)
    }
    return $out
}

$members = @("Joe Bloggs", "Some Dude", "The Dude")

$memberDetails = Get-OutList -MemberCollection $members
Write-Output $memberDetails

如果你在 $out 传回之前添加一个断点,你会看到有 3 条记录,如果你继续步进,你应该看到 $memberDetails 将有 6 条记录(前 3 条空白)。

编辑 #2 使用 Generic.List 而不是 ArrayList 时似乎没有这样的问题。使用 $out = [System.Collections.Generic.List[PSObject]]::new() 而不是 $out = New-Object 'System.Collections.ArrayList',它工作得很好。

使用 $out = [System.Collections.Generic.List[PSObject]]::new() 而不是 $out = New-Object 'System.Collections.ArrayList',它工作得很好。没有多余的毯子。

您的问题源于 System.Collections.ArrayList.Add() 方法具有 return 值(索引添加对象的位置)并且 return 值 成为函数“return 值”的一部分,即 输出对象流 .

这是 PowerShell 的 隐式输出行为 的表现形式,在 .

中进行了解释

值得注意的是,return 不是 在 PowerShell 中从函数生成 output 所必需的 - 任何语句都可以生成输出。 return $foo 只是 Write-Output $foo; return 的语法糖,或者依赖于 隐式 输出行为,$foo; return

因此,立即修复你的问题是丢弃(抑制)你的隐式输出$out.Add($memberDetail)调用,这通常是通过将调用分配给$null来完成的(还有其他方法,在中讨论):

# Discard the unwanted output from .Add()
$null = $out.Add($memberDetail)

, you indirectly applied that fix by switching to the System.Collections.Generic.List`1列表类型,其.Add()方法有return值。正是出于这个原因,以及 强类型 列表元素的能力,System.Collections.Generic.List`1 通常优于 System.Collections.ArrayList


退一步:

在像您这样的简单情况下,整个函数输出将被捕获在一个列表中,您可以利用 output-stream 行为并简单地 发出每个输出对象 单独 从您的函数(从ForEach-Object 脚本块)和PowerShell 收集单个对象list-like 数据结构中的 emitted,它将是一个常规的 PowerShell array,即 [object[] 类型(即 fixed-size 类似于 System.Collections.ArrayList):

这样不仅更方便,而且性能也更好。

应用于您的示例函数:

function Get-OutList {
  param (
      [Parameter(Position=0,mandatory=$true)]
      [PSObject]$MemberCollection
  )

  $MemberCollection | ForEach-Object {
    $first, $last = -split $_
    # Construct and implicitly output a custom object.
    [pscustomobject] @{
      FirstName = $first
      LastName = $last
      Email = 'Three'
      Status = 'Four'
    }
  }
  # No need for `return` - all output objects were emitted above.
}

$members = @("Joe Bloggs", "Some Dude", "The Dude")

# By assigning the function's output stream to a variable,
# the individual output objects are automatically collected in an array
# (assuming *two or more* output objects; if you want an array even if 
# only *one* object is output, use [Array] $memberDetails = ...)
$memberDetails = Get-OutList -MemberCollection $members

# Output (implicitly).
$memberDetails 

另请注意使用 [pscustomobject] @{ ... } 语法糖来简化 custom-object 创建 - 请参阅概念性 about_Object_Creation 帮助主题。