Powershell 本地数组重新初始化

Powershell local array re-initialization

我在我的一个脚本中发现了一个错误,看起来 PowerShell(2.0 版)在再次输入函数时没有重新初始化本地数组。这是一个极简主义的例子:

function PrintNumbersUntilX {
    param (
        [ Parameter( Mandatory = $true ) ] $X
    )
    $Number = 0
    while( $Number -le $X ) {
        Write-Host $Number
        $Number++
    }
}

function PrintNumbersUntilY {
    param (
        [ Parameter( Mandatory = $true ) ] $Y
    )
    $Number = ( , 0 )
    while( $Number[ 0 ] -le $Y ) {
        Write-Host $Number[ 0 ]
        $Number[ 0 ]++
    }
}

function Main {
    Write-Host 'X-3: '
    PrintNumbersUntilX -X 3
    Write-Host 'X-12: '
    PrintNumbersUntilX -X 12
    Write-Host 'Y-3: '
    PrintNumbersUntilY -Y 3
    Write-Host 'Y-12: '
    PrintNumbersUntilY -Y 12
}

Main

输出(换行符替换为 space):

X-3:  0 1 2 3 X-12:  0 1 2 3 4 5 6 7 8 9 10 11 12 Y-3:  0 1 2 3 Y-12:  4 5 6 7 8 9 10 11 12

PrintNumbersUntilX 的工作方式与我期望的局部变量相同,但 PrintNumbersUntilY 不会重置(重新初始化)$Number

这个错误在以后的版本中是否已修复,或者这是一个 'feature',如果是,它叫什么,我该如何摆脱它?

您可以在 2.0 中通过使用显式数组子表达式 @() 来初始化 $Number:

来解决它
$Number = @( , 0 )

你知道它,但让我们明确一点,观察到的行为显然是 a - 非常严重的错误 - 在 Windows PowerShell 的 2.0 版中:

  • 从表面上看,将直接构造的单元素数组赋值给局部变量在随后对给定脚本或函数的调用中被“优化”掉了。

  • 如果可能,请考虑升级您的 PowerShell 版本:Windows PowerShell v2.0 于 2009 年 10 月发布;当前版本是 v5.1.

    • v2.0 肯定不会 看到任何错误修复,而 v5.1 - 这是最后一个 Windows PowerShell 版本 - 不再积极开发 ,但 可能 会看到错误修复- 取决于给定错误的严重程度。

    • 相比之下,跨平台 PowerShell (Core) v6+ 版本是 Windows PowerShell 的 后继者 正在积极开发并维护.


解决方法:

选项1: 使用@(...)array-subexpression operator in lieu of the unary form of ,, the array constructor operator

$Number = @( 0 )

这是 .

的简化版,也许不那么晦涩难懂

要明确:

  • 您构建单元素数组的方法 - , 0 - 在 v3+ 中可以正常工作,在概念上更可取 ,因为 - 与 , 运算符不同 - @(...) 实际上并不 构造 数组 ,它“保证”它们,松散地说:也就是说,@( @( 0 ) )@( , 0 )@( 0 ) 相同,并导致(非嵌套)单元素数组。相比之下,, , 0确实构造了一个嵌套数组(一个单元素数组,其元素是另一个元素为0的单元素数组)。

  • @(...) 从未打算用于声明 数组文字:它的目的是将封闭的命令或表达式的输出收集到一个新构造的[object[]]数组中,即使只有一个对象被输出,因此确保 situationally 来自管道的单对象输出仍然被视为 作为数组(假设单对象输出默认收集 原样,没有数组包装器)。

    • 在直到 v5.0 的 PowerShell 版本中,将 @(...) 与数组文字一起使用不仅 没有必要 ,而且 效率低下:根据描述的机制,在幕后创建了一个额外数组。

    • 因为 @(...) 用于数组初始化的“标签外”使用非常普遍,v5.1 引入了优化:@(...) 在幕后进行了有效优化在 @( 1, 2 ) 等表达式中,使其与 1, 2

      实际上相同
    • 恰恰是缺少这个优化使得@( 0 )变通方法在v2.0中有效:因为评估被推迟到运行时,错误分配“优化”未执行。

  • 有关 @(...)

    的更多信息,请参阅 的底部部分

选项2: Type-constrain变量作为数组,具有很强的输入 [int[]] 甚至可以提高效率(尽管影响可以忽略不计):

[array] $Number = 0 # Note: "," isn't even needed then; constructs [object[]] array
   
# Or, more efficiently, with strong typing:
# (The above constructs an object[] array in which value types must be boxed.)
[int[]] $Number = 0