尝试并未能通过引用传递自定义对象数组

Trying and failing to pass an array of custom objects by reference

我正在我的 powershell 脚本中创建一个自定义对象数组

$UnreachablePCs = [PSCustomObject]@()

然后我将其传递给这样的函数

function GetComputerData {
param (
    $Computers, [ref]$unreachable
)
...
$unreachablePC = [PSCustomObject]@{
            ComputerName = $i.DNSHostName
            CPU = "n/a"
            Cores = "n/a"
            IP = "n/a"
            Memory = "n/a"
            Uptime = "n/a"
            OS = "n/a"
            Board = "n/a"
        }
    $UnreachablePCs += $unreachablePC
    Write-Output $UnreachablePCs.Count
...
}


GetComputerData -Computers $TraderWorkstations -unreachable ([ref]$UnreachablePCs)
Write-Output $UnreachablePCs.Count

$TraderWorkstations 是在函数中迭代的 pc 列表。所有不可访问的 pc 都被添加到函数的 else 分支中的 $UnreachablePCs 数组中。在函数中,我调用的 .Count 将随着工作站添加到列表中而增加。但是在调用函数之后,最终的 .Count 返回 0。我在这里缺少什么?

不要在 PowerShell 代码中使用 [ref] 参数: [ref] 的目的是方便 调用 .NET API 方法;在 PowerShell 代码中,它在语法上很笨拙,可能会导致细微的错误,例如您的情况 - 请参阅 指南,了解何时 [ref] 使用是合适的。

相反,让你的函数输出组成结果数组的对象(可能一个接一个),并且让 PowerShell 在数组中为您收集它们:

function GetComputerData {
param (
    $Computers # NO [ref] parameter
)
   # ...
   # Output (one or more) [pscustomobject] instances.
   [PSCustomObject]@{
            ComputerName = $i.DNSHostName
            CPU = "n/a"
            Cores = "n/a"
            IP = "n/a"
            Memory = "n/a"
            Uptime = "n/a"
            OS = "n/a"
            Board = "n/a"
        }
  # ...
}

# Collect the [pscustomobject] instances output by the function
# in an array.
$UnReachablePCs = @(GetComputerData -Computers $TraderWorkstations)

@()array-subexpression operator,总是创建一个[object[]]数组。要改为创建强类型数组,请使用:
[pscustomobject[]] $unreachablePCs = GetComputerData -Computers $TraderWorkstations

重要:

    由于 PowerShell 的 隐式输出功能
  • [PSCustomObject]@{ ... 直接从函数生成 output,其中任何未捕获或重定向输出的命令或表达式都会自动贡献于封闭函数的(脚本的)输出(写入 PowerShell 的 success output stream) - see 以获取详细信息。写入函数成功输出流的所有对象都由变量赋值捕获比如$UnReachablePCs = ...

  • Write-Outputexplicit (很少需要)写入成功输出流的方式,这也意味着您不能将它用于临时调试输出 到显示 ,因为它的输出也成为函数“return 值”(发送到成功输出流的对象)的一部分。

  • 如果您想要 to-display 不“污染”成功输出流的输出,请使用 Write-Host. Preferably, use cmdlets that target other, purpose-specific output streams, such as Write-Verbose and Write-Debug,尽管它们都需要 opt-in 生成可见输出(参见链接文档)。


至于你原来方法的问题

$UnreachablePCs = [PSCustomObject]@()

这不会创建自定义对象数组。 相反,它创建了一个 [object[]] 数组(无用,大部分是无形的)包裹在 [psobject] 实例中。[1]

改用以下内容:

[PSCustomObject[]] $UnreachablePCs = @()

至于使用 [ref] 和更新为 += 的数组变量:

  • 从根本上说,您需要通过分配给 .Value 属性[= 来更新包含 [ref] 实例的(参数)变量129=],而不是整个 [ref] 实例($UnreachablePCs.Value = ... 而不是 $UnreachablePCs = ...

  • 但是,+= 技术应该避免 除了小数组,因为每个 += 操作都需要创建一个 new array behind(包含原始元素和新元素),这是必需的,因为数组是fixed-size数据结构。

    • 两者之一:使用有效可扩展的列表数据类型,例如 [System.Collections.Generic.List[PSCustomObject]] 并通过其 .Add() 方法扩展它(事实上,如果您事先创建列表实例,你可以将它作为一个普通的(非[ref])参数传递给一个非[ref]参数,并且该函数仍然会直接更新列表,因为调用[=时在同一个列表实例上操作39=] - 也就是说,顶部的 output 方法通常仍然更可取):

      $unreachablePCs = [System.Collections.Generic.List[PSCustomObject]] @()
      foreach ($i in 1..2) {
        $unreachablePCs.Add([pscustomobject] @{ foo = $i })
      }
      
    • 或者 - 最好 - 如果可能:使用 PowerShell 的循环语句作为 表达式 ,并让 PowerShell 为您收集数组中的所有输出(也显示为上面整个函数的输出);例如:

      # Automatically collects the two custom objects output by the loop.
      [array] $unreachablePCs = foreach ($i in 1..2) {
         [pscustomobject] @{ foo = $i }
      }
      

[1] 这种行为很不幸,但源于类型加速器 [pscustomobject][psobject] 相同的 [pscustomobject] 转换仅在创建单个自定义对象 文字 的上下文中才有意义,即如果后跟哈希表(例如 [pscustomobject] @{ foo = 1 })。在所有其他情况下,[psobject] 中几乎看不见的包装发生;例如,[pscustomobject] @() -is [object[]]$true - 即结果的行为类似于常规数组 - 但 [pscustomobject] @() -is [psobject] 也是 $true,表示存在 [psobject] 包装器。