【问题标题】:Blank records when returning ArrayList of PSObjects from function从函数返回 PSObjects 的 ArrayList 时的空白记录
【发布时间】:2022-01-31 03:41:59
【问题描述】:

所以我正在重构一个 Powershell 脚本并将很多东西移动到函数中。当我从带有return $out 的函数(称为Get-OutList)返回一个包含42 个PSObjects 的ArrayList 时,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 会将每条记录枚举回调用函数的位置,但我尝试了一些方法无济于事:

  • PS v5.1.x 和 v7.3.0 中的行为相同。
  • 返回 return @($out) 没有任何区别(即,System.Object 有 42 条空白记录,后面跟着我的 42 条原始记录,总共 84 条记录)。
  • 返回 return Write-Output -NoEnumerate $out 会导致 System.Object 有 42 条空白记录,而我的 42 条原始记录嵌套在第 43 条记录中。
  • 使用$results = [System.Collections.ArrayList](Get-OutList) 将函数调用的结果键入为ArrayList 没有区别。

为什么我不能让我的对象与从函数返回之前的对象相同?任何帮助将不胜感激!

编辑#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',它工作得很好。

【问题讨论】:

  • 在不知道你传递给你的函数的情况下很难说,另一方面,你的函数让我觉得你过于复杂了。提供一个可重复的最小示例,可能会对您有所帮助。
  • @NathanC - 你真的不应该输出一个集合,除非没有其他方法可以完成这项工作。输出一个对象并允许调用者将事物收集到他们想要的任何集合中会更好[也更容易]。 ///// 实际上,我想知道您是否看到了按引用与按值的示例。您可能需要查看 [icky!讨厌! yucky!] 明确设置集合 $Var 的范围。
  • 只是为了避免混淆:您看到的额外输出不是 blank 对象,而是来自 @ 的返回值 integers 987654329@ 方法调用,无意中“污染”了函数的输出流。

标签: powershell arraylist


【解决方案1】:

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

这是PowerShell的隐式输出行为的体现,在this answer中有解释。

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

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

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

In your own answer,你间接通过切换到System.Collections.Generic.List`1列表类型应用了该修复,其.Add()方法具有no返回值。正是由于这个原因,加上强类型列表元素的能力,System.Collections.Generic.List`1 通常比System.Collections.ArrayList 更可取


退后一步:

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

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

应用于您的示例函数:

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] @{ ... } 语法糖来简化自定义对象的创建 - 请参阅概念性的 about_Object_Creation 帮助主题。

【讨论】:

    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 2017-01-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-14
      • 1970-01-01
      • 1970-01-01
      • 2011-06-26
      • 1970-01-01
      相关资源
      最近更新 更多