在 PowerShell 中,每次执行脚本时,数组都会变大

问题描述:

所以我从 PowerShell 开始,这对我来说很奇怪.我在最后一行放置了一个断点,当我运行脚本时 $newArray 是abc"然后它在 Write-Output 中暂停,我停止调试器.如果我再次运行它,$newArray 是abcabc";等等.一方面,我认为我做错了什么,因为这种行为对我来说太奇怪了,就像尽管停止了调试器,它仍然存储在内存中.其次,我希望 $newArray 是一个数组,而不是将值连接在一起的单个字符串.有什么线索吗?

So I'm starting with PowerShell and this is odd to me. I put a breakpoint in the last line and when I run the script $newArray is "abc" then it pauses in Write-Output and I stop the debugger. If I run it again, $newArray is "abcabc" and so on. For one, I think I'm doing something wrong as this behavior is so weird to me, like it stores in memory despite of stopping the debugger. And secondly I would expect $newArray to be an array and not a single string with the values concatenated. Any clues?

$array = "a", "b", "c";
$newArray;
foreach ($item in $array )
{
    $newArray += $item
}
Write-Output "Just before ending script";

tl;dr

  • 为了使用 += 迭代构建一个数组,您必须(重新)初始化目标变量作为一个空数组:$newArray = @();如果不这样做会发生什么,请参阅底部部分.

  • In order to use += to iteratively build up an array, you must (re)initialize the target variable as an empty array: $newArray = @(); see the bottom section for what happens if you don't.

  • Re repeated debugging passes: unfortunately, both the obsolescent Windows PowerShell ISE and the PowerShell extension for Visual Studio Code run in the same session, so that subsequent invocations can be affected by the state of previous ones. However, the Visual Studio PowerShell extension can be configured to use a new session every time - see this answer.

也就是说,通常最好不要使用+=,因为它效率低下:它需要创建一个new 每次迭代的幕后数组,因为 .NET 数组是不可变数据结构(就元素计数而言).

That said, it's usually better not to use +=, because it is inefficient: it requires creating a new array behind the scenes in every iteration, because .NET arrays are immutable data structures (in terms of element count).

  • 在您的简单情况下,您可以简单地使用以下内容创建 $array 的(浅)副本,通过 @()array-subexpression 运算符,隐式克隆一个数组:
    • $newArray = @($array)
    • In your simple case, you could simply use the following to create a (shallow) copy of $array, via @(), the array-subexpression operator, which implicitly clones an array:
      • $newArray = @($array)
      • [array] $newArray = foreach ($array 中的 $item) { $item + '!'}
      • 注意 [array] 类型约束,它确保 $newArray 接收一个数组,即使 foreach 循环恰好输出 一个对象;或者,您可以将循环包装到 @(...)
      • [array] $newArray = foreach ($item in $array) { $item + '!' }
      • Note the [array] type constraint, which ensures that $newArray receives an array even if the foreach loop happens to output just one object; alternatively, you could have wrapped the loop into @(...)

      += 的工作原理:

      How += works:

      简而言之:如果您使用 += 添加到未初始化变量的第一个值是一个字符串,则该字符串是按原样存储,所有后续的+= 操作都执行字符串连接;具体:

      In short: if the first value you add to an uninitialized variable with += is a string, that string is stored as-is, and all subsequent += operations perform string concatenation; specifically:

      • $newArray += $item$newArray = $newArray + $item 的语法糖.

      如果 $newArray 没有被定义,它的隐含值为 $null[1]

      If $newArray has not been defined, its implied value is $null[1]

      由于您的 $item 值是字符串 ([string]),$newArray += $item 在第一次迭代中相当于 $newArray = $null + a",它分配 a" - 一个 string - 给 $newArray(带有 [string] RHS,LHS 上的 $null 被视为空字符串,执行 在这种情况下有效返回 RHS 字符串的字符串连接).

      Since your $item values are strings ([string]), $newArray += $item in the first iteration amounts to $newArray = $null + "a", which assigns "a" - a single string - to $newArray (with a [string] RHS, $null on the LHS is treated like an empty string, performing string concatenation that effectively returns the RHS string in this case).

      第二次迭代等于 $newArray = "a";+ "b",假设 LHS 是一个字符串 - 再次执行字符串连接,这样 $newArray之后包含 "ab".

      The second iteration amounts to $newArray = "a" + "b", which - given that the LHS is a string - again performs string concatenation, so that $newArray contains "ab" afterwards.

      第三次(以及任何假设的后续迭代)只是继续追加到存储在 $newArray 中的字符串.

      The third (and any hypothetical subsequent iterations) simply keep appending to the string stored in $newArray.

      一般来说:

      • $initiallyUninitialized += 第一次 +=迭代中,$null + 被评估,它将 存储在 $initiallyUninitialized 中并有效地将其键入为whatever type 恰好是.

      • In the first += iteration of $initiallyUninitialized += <value>, $null + <value> is evaluated, which stores <value> in $initiallyUninitialized and effectively types it as whatever type <value> happens to be.

      在随后的 += 迭代中,$initiallyUninitialized 值的当前类型决定了隐含的 $initiallyUninitialized + 操作 - 通常,但不一定,保留该类型:

      In subsequent += iterations, the then-current type of the $initiallyUninitialized value determines the result of the implied $initiallyUninitialized + <value> operation - which typically, but not necessarily, preserves that type:

      • 数字类型的情况下,该类型可能会自动加宽:
        • $a += [int]::MaxValue;$a += [int]::MaxValue - $a 现在是 [double] 类型.
        • The type may automatically widen in the case of a numeric type:
          • $a += [int]::MaxValue; $a += [int]::MaxValue - $a is now of type [double].
          • $a += Get-Date;$a += Get-Date - 导致语句终止错误,因为两个 [datetime] 实例不能添加(+).
          • $a += Get-Date; $a += Get-Date - causes a statement-terminating error, because two [datetime] instances cannot be added (+).

          [1] 默认情况下适用 (Set-StrictMode -Off);使用 Set-StrictMode -Version 1 或更高版本,您实际上会得到一个语句终止错误.

          [1] This applies by default (Set-StrictMode -Off); with Set-StrictMode -Version 1 or higher, you'd actually get a statement-terminating error.