【问题标题】:[System.Collections.Generic.List[string]] as return value[System.Collections.Generic.List[string]] 作为返回值
【发布时间】:2021-05-20 19:31:45
【问题描述】:

我需要/想要从函数返回 [System.Collections.Generic.List[string]],但它被覆盖到 System.Object[]

我有这个

function TestReturn {
    $returnList = New-Object System.Collections.Generic.List[string]
    $returnList.Add('Testing, one, two')

    return ,@($returnList)
}

$testList = TestReturn
$testList.GetType().FullName

将其作为System.Object[] 返回,如果我将返回行更改为

return [System.Collections.Generic.List[string]]$returnList

return [System.Collections.Generic.List[string]]@($returnList)

如果列表中有一项,则返回[System.String],如果有多个项,则返回System.Object[],在这两种情况下。列表有什么奇怪的地方不能用作返回值吗?

现在,奇怪的是(我认为)如果我像这样键入接收值的变量,它确实可以工作。

[System.Collections.Generic.List[string]]$testList = TestReturn

但这似乎是一种奇怪的强制转换,其他数据类型不会发生这种情况。

【问题讨论】:

  • 我猜默认情况下,PowerShell 会假定System.Array,但如果你这样做[collections.generic.list[string]]$testList = TestReturn,即使函数返回一个字符串,它也应该可以正常工作。

标签: powershell return-type generic-list


【解决方案1】:

如果您删除数组子表达式 @(...) 并在前面加上一个逗号。下面的代码似乎工作:

function TestReturn {
    $returnList = New-Object System.Collections.Generic.List[string]
    $returnList.Add('Testing, one, two')

    return , $returnList
}

$testList = TestReturn
$testList.GetType().FullName

注意:从技术上讲,这会导致 [Object[]] 返回一个类型为 [System.Collections.Generic.List[string]] 的元素。但同样是因为隐式展开它有点欺骗 PowerShell 输入所需的内容。

在您后面的观点中,[Type]$Var 类型的语法限制了变量。它基本上锁定了该变量的类型。因此,对 .GetType() 的后续调用将返回该类型。

这些问题是由于 PowerShell 如何在输出时隐式展开数组。典型的解决方案(在某种程度上取决于类型)是在返回之前使用 , 或确保调用端的数组,或者通过类型约束变量,如您的问题中所示,或者包装或转换返回本身。后者可能看起来像:

$testList = [System.Collections.Generic.List[string]]TestReturn
$testList.GetType().FullName

为确保在可以进行标量返回时的数组,并且假设您没有在 return 语句之前使用 , ,您可以在调用端使用数组子表达式:

$testList = @( TestReturn )
$testList.GetType().FullName

我相信this answer 处理类似的问题

【讨论】:

  • 看来我忘记了如何正确处理展开行为,然后将其归因于类型。我一直在想我可能也有管道解决方案。我将所有代码移至类以完全避免管道,并且我刚刚验证了展开行为仅在函数中。正如预期的那样,课程不会做意想不到的事情。我正在对当前基于函数的代码进行短期修复,但我期待着转向类。
  • 管道污染。 Spellcheck 和我,我们相处不来。而且已经很晚了。
  • 我很高兴能充当您的提醒。顺便说一句,“污染”我用了同一个词here
【解决方案2】:

除了Steven's very helpful answer,您还可以选择使用[CmdletBinding()] 属性,然后调用$PSCmdlet.WriteObject。默认情况下,它将保留类型。

function Test-ListOutput {
    [CmdletBinding()]
    Param ()
    Process {
        $List = New-Object -TypeName System.Collections.Generic.List[System.String]
        $List.Add("This is a string")
        $PSCmdlet.WriteObject($List)
    }
}
$List = Test-ListOutput
$List.GetType()
$List.GetType().FullName

对于数组,你应该指定类型。

function Test-ArrayOutput {
    [CmdletBinding()]
    Param ()
    Process {
        [Int32[]]$IntArray = 1..5
        $PSCmdlet.WriteObject($IntArray)
    }
}
$Arr = Test-ArrayOutput
$Arr.GetType()
$Arr.GetType().FullName

默认情况下PSCmdlet.WriteObject() 的行为是不枚举集合($false)。如果您将值设置为$true,您可以看到管道中的行为。

function Test-ListOutput {
    [CmdletBinding()]
    Param ()
    Process {
        $List = New-Object -TypeName System.Collections.Generic.List[System.String]
        $List.Add("This is a string")
        $List.Add("This is another string")
        $List.Add("This is the final string")
        $PSCmdlet.WriteObject($List, $true)
    }
}

Test-ListOutput | % { $_.GetType() }

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object
True     True     String                                   System.Object
True     True     String                                   System.Object

(Test-ListOutput).GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array


# No boolean set defaults to $false
function Test-ListOutput {
    [CmdletBinding()]
    Param ()
    Process {
        $List = New-Object -TypeName System.Collections.Generic.List[System.String]
        $List.Add("This is a string")
        $List.Add("This is another string")
        $List.Add("This is the final string")
        $PSCmdlet.WriteObject($List)
    }
}

Test-ListOutput | % { $_.GetType() }

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     List`1                                   System.Object

(Test-ListOutput).GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     List`1                                   System.Object

只是想补充一些关于我通常在函数中使用什么以及如何控制行为的信息。

【讨论】:

  • 伙计,我真希望我几年前就知道这个把戏。多年来,我浪费了很多时间来追踪管道污染。很多时候我希望有一个指令来禁用管道作为函数返回机制,但它确实存在。我现在有这么多代码,最好只是出于其他原因将其重构为类并获得可预测的行为作为奖励。但是学习新东西总是好的。 :)
  • 我是从编写 C# cmdlet 才知道的。它真的很有用。我会小心使用 PowerShell 类。他们基本的事情做得很好,但在 GitHub 上仍然有很多围绕他们的未解决的问题,我相信他们主要是为了支持 DSC 而引入的。同样,我仍然使用 C# 编写任何类。
  • 我的需求很简单,我的知识也更简单。到目前为止,课程对我来说是一种享受,但对我来说学习不同的思考方式是一个巨大的学习曲线。希望我不会遇到任何交易破坏者。可能需要在 GitHub 上闲逛一下,看看我是否可以解决任何潜在的问题。
猜你喜欢
  • 2021-11-25
  • 1970-01-01
  • 1970-01-01
  • 2018-12-03
  • 2014-03-05
  • 1970-01-01
  • 2013-12-10
  • 1970-01-01
  • 2020-06-16
相关资源
最近更新 更多