Powershell:如何禁用整数数学上的自动 int32 类型转换?

Joa*_*hal 1 powershell types casting type-conversion

这是一个例子,真实的东西是一个大的New-Object 'object[,]'二维数组:

PS C:\> $a=[int16]10
PS C:\> $a.GetType().Name
Int16
PS C:\> $a++
PS C:\> $a.GetType().Name
Int32

PS C:\> $a=[int16]10
PS C:\> $a.GetType().Name
Int16
PS C:\> $a=$a+[int16]1
PS C:\> $a.GetType().Name
Int32
Run Code Online (Sandbox Code Playgroud)

我可以在此类事情中强制使用类型,但这实际上是数学完成后的转换,并且会使完整代码减慢约 10%,因此它是无用的:

PS C:\> $a=[int16]10
PS C:\> $a.GetType().Name
Int16
PS C:\> $a=[int16]($a+1)
PS C:\> $a.GetType().Name
Int16
Run Code Online (Sandbox Code Playgroud)

在使用任何数学时,是否没有办法阻止从 byte 和 int16 到它喜爱的 int32 的自动转换?

至于@mklement0 的评论,我将示例扩展到用例。请注意,该板上任何位置的值都不会超过“8”:

# Init board
$BoardXSize = [int]100
$BoardYSize = [int]100
$Board = New-Object 'object[,]' $BoardXSize,$BoardYSize
for ($y=0;$y -lt $BoardYSize;$y++) {
    for ($x=0;$x -lt $BoardXSize;$x++) {
        [int16]$Board[$x,$y] = 0
    }
}
Run Code Online (Sandbox Code Playgroud)

测试:

PS C:\> ($Board[49,49]).GetType().Name
Int16
PS C:\> $Board[49,49]++
PS C:\> ($Board[49,49]).GetType().Name
Int32

PS C:\> [int16]$Board[49,49]=0
PS C:\> ($Board[49,49]).GetType().Name
Int16
PS C:\> [int16]$Board[49,49]++
0
PS C:\> ($Board[49,49]).GetType().Name
Int32

PS C:\> [int16]$Board[49,49]=0
PS C:\> ($Board[49,49]).GetType().Name
Int16
PS C:\> $Board[49,49] += [int16]1
PS C:\> ($Board[49,49]).GetType().Name
Int32

PS C:\> [byte]$Board[49,49]=0
PS C:\> ($Board[49,49]).GetType().Name
Byte
PS C:\> $Board[49,49] += [byte]1
PS C:\> ($Board[49,49]).GetType().Name
Int32
Run Code Online (Sandbox Code Playgroud)

然而,当对其工作的运算符极其挑剔时,仅“++”似乎会失败:

PS C:\> [int16]$Board[49,49]=0
PS C:\> ($Board[49,49]).GetType().Name
Int16
PS C:\> [int16]$Board[49,49] += [int16]1
PS C:\> ($Board[49,49]).GetType().Name
Int16

PS C:\> [byte]$Board[49,49]=0
PS C:\> ($Board[49,49]).GetType().Name
Byte
PS C:\> [byte]$Board[49,49] += [byte]1
PS C:\> ($Board[49,49]).GetType().Name
Byte
Run Code Online (Sandbox Code Playgroud)

但由于这总是在数学完成后进行转换,因此不会提高速度。但已经解决了。

mkl*_*nt0 5

\n

您可以通过将类型文字放置在初始)赋值的左侧来对变量进行类型约束:

\n
# Note how [int16] is to the *left* of $a = ...\nPS> [int16] $a = 10; ++$a; $a += 1; $a, $a.GetType().Name\n12\nInt16\n
Run Code Online (Sandbox Code Playgroud)\n

请注意,这与在静态类型语言(例如 C#)中键入变量的工作方式不同,例如:不是静态锁定指定类型,而是对每个赋值执行强制转换,以强制分配给该类型的值。

\n

例如, with[int16] $a = 10是原始分配,$a += 1实际上与分配 相同[int16] ($a + 1)

\n

这也意味着您可以自由地分配任何类型的值,只要它们可以转换为约束类型即可;例如:

\n
# These work, because they are implicitly cast (converted) to [int16]\n$a += 1.2\n$a += \'42\'\n
Run Code Online (Sandbox Code Playgroud)\n

请参阅下一节有关数组的内容。

\n

数组应用类型约束:

\n

对于(一维)数组

\n
# Strongly types the array as storing [int16] instances\n# *and* type-constrains the $arr variable.\nPS> [int16[]] $arr = 10, 11; ++$arr[0]; $arr[1] += 1; $arr; $arr.ForEach(\'GetType\').Name\n11\n12\nInt16\nInt16\n
Run Code Online (Sandbox Code Playgroud)\n

二维数组(在 PowerShell 中很少见

\n
# Strongly types the 2D array as storing [int16] instances,\n# but does *not* type-constrain the $arr2d *variable.\nPS> $countDim1, $countDim2 = 2, 3;\n    $arr2d = [int16[,]]::new($countDim1, $countDim2);\n    ++$arr2d[0,0]; $arr2d[0,0].GetType().Name\nInt16\n
Run Code Online (Sandbox Code Playgroud)\n

请注意,这确实创建了强静态类型的 .NET 数组,但分配给它们的元素再次应用了 PowerShell 的灵活类型转换(例如,$arr[0] = \'42\'工作正常)。

\n

另请注意,为简洁起见,二维数组示例实际上并未对变量的数据类型进行类型约束因为赋值左侧没有类型文字;因此,您仍然可以用任何类型的任意新值替换整个数组(例如,$arr2d = \'foo\');要也执行类型约束,您必须执行以下操作:

\n
# Creates a 2D [int16] array *and* type-constrains $arr2D to\n# such arrays.\n[int16[,]] $arr2d = [int16[,]]::new($countDim1, $countDim2)\n
Run Code Online (Sandbox Code Playgroud)\n

最后,请注意,您不能对数组的各个元素或对象的(可写)属性进行类型约束;换句话说:只有作为PowerShell 变量的L 值才能受到类型约束;[1]在所有其他情况下,执行简单的临时转换;例如,给定一个[object[]]array $a = 1, 2,以下两个语句是等效的:

\n
# Cast\n$a[0] = [int] 42 \n\n# !! A mere cast too, because the assignment target is an \n# !! *array element*, which cannot be type-constrained.\n[int] $a[0] = 42\n
Run Code Online (Sandbox Code Playgroud)\n
\n

与 C# 等语言不同的是,PowerShell 会自动扩展数值运算中的类型

\n

这种扩大不仅适用于不同(数字)类型的操作数,而且还适用于结果太大而无法适应(两个中较大的)输入类型的情况,并且它有两个令人惊讶的方面:

\n
    \n
  • 应用最小宽度 ( ),这意味着小于的类型始终加宽到[int]System.Int32[int][int],即使没有必要,正如您所观察到的

    \n
    # Implicitly widens to [int], even though not necessary.\n# Note that the [byte] cast is only applied to the LHS\nPS> ([byte] 42 + 1).GetType().Name\nInt32\n
    Run Code Online (Sandbox Code Playgroud)\n
  • \n
  • 如果结果太大而无法适应(两个中较大的)输入类型,则结果总是会扩展为[double]( System.Double)(!)

    \n
    # Implicitly widens to [double](!), because adding 1\n# to the max. [int] value doesn\'t fit into an [int].\nPS> ([int]::MaxValue + 1).GetType().Name\nDouble\n\n# NO widening, because the large input type is a [long] (System.Int64)\n# (suffix L), so the result fits into a [long] too.\nPS> ([long] [int]::MaxValue + 1).GetType().Name\nInt64\n
    Run Code Online (Sandbox Code Playgroud)\n
  • \n
  • 这种立即扩展到[double]in 表达式(不考虑更大的整数类型)的方式与 PowerShell 中解析数字文字的方式不同,在 PowerShell 中使用更大的整数类型但请注意,PowerShell 从不选择无符号类型):

    \n
    # 2147483648 = [int]::MaxValue + 1, so [long] (System.Int64)\n# is chose as the literal\'s type. \n(2147483648).GetType().Name\nIn64\n
    Run Code Online (Sandbox Code Playgroud)\n
      \n
    • 请注意,数字文字还可以具有类型后缀来显式指定其类型,例如lfor[long]dfor [decimal]; 例如,2147483648d.GetType().Name返回Decimal.
    • \n
    \n
  • \n
\n
\n

可选阅读:访问强类型与非类型 ( ) 数组中的数组元素的性能[object[]]

\n

例如,PowerShell 的常规数组类型(由,和运算符创建)是,即“无类型”数组,作为 .NET 类型层次结构的根,可以存储任何数据类型的值 - 甚至允许在给定的单个数组中混合不同类型。@(...)[object[]][object]

\n

虽然这提供了很大的灵活性,但它会影响:

\n
    \n
  • 类型安全:根据用例,可能需要确保所有元素具有相同的类型;如果是这样,请使用强类型数组。

    \n
  • \n
  • 内存效率:值类型实例必须包装在[object]实例中进行存储,这个过程称为装箱,并且在访问时需要拆开包装,称为拆箱

    \n
  • \n
  • 运行时性能:令人惊讶的是,与装箱和拆箱的开销不同,几乎没有任何影响性能优势,而且对于 2D 数组,性能甚至会受到影响。

    \n
  • \n
\n

以下是数组获取和设置访问基准测试的结果,将具有各种值类型的一维和二维数组与普通数组进行比较。object元素的一维和二维数组进行比较:

\n
    \n
  • 使用 100 万个元素的一维数组和 1000 x 1000 的二维数组对 15 次运行进行平均。

    \n
  • \n
  • 基准测试源代码如下。

    \n
      \n
    • 要自己运行代码,您需要首先Time-Command这个要点下载并定义函数

      \n
    • \n
    • 假设您已经查看了链接的 Gist 的源代码以确保它是安全的(我个人可以向您保证,但您应该始终检查),您可以直接安装它,如下所示:

      \n
      irm https://gist.github.com/mklement0/9e1f13978620b09ab2d15da5535d1b27/raw/Time-Command.ps1 | iex\n
      Run Code Online (Sandbox Code Playgroud)\n
    • \n
    • PowerShell 中的基准测试远非一门精确的科学。由于基准测试是基于挂钟的,因此最好对多次运行进行平均,并确保系统不会(太)忙于运行其他事情。

      \n
    • \n
    \n
  • \n
  • 绝对时间会根据许多因素而变化,但该Factor列应该提供相对性能的感觉 - 小数点后第二位的差异可能是偶然的(例如,1.001.03),并且此类排名可能会在重复调用中交换位置。

    \n
  • \n
  • 以下结果得出一些结论,这些结果是在 Windows 10 计算机上的PowerShell (Core) 7.2.2上运行的- Windows PowerShell性能特征可能有所不同

    \n
      \n
    • 一维数组(迄今为止 PowerShell 中最常见的数组类型):

      \n
        \n
      • 获得微不足道的快一些。
      • \n
      • 更新中如果强类型,
      • \n
      \n
    • \n
    • 二维数组(在 PowerShell 中很少见):

      \n
        \n
      • 获取速度稍慢如果强类型,
      • \n
      • 更新速度比较快一些如果强类型,
      • \n
      \n
    • \n
    • 注意:虽然下面没有 Windows PowerShell 结果,但在我的测试中,PowerShell (Core) 7+ 中数组访问的速度似乎增加了一倍

      \n
    • \n
    \n
  • \n
\n
# Sample results on a Windows 10 machine running PowerShell 7.2.2\n\n============== 1D: GET performance (avg. of 15 runs)\n\nFactor Secs (15-run avg.) Command\n------ ------------------ -------\n1.00   0.482              # [byte[]]\xe2\x80\xa6\n1.00   0.483              # [int[]] (Int32)\xe2\x80\xa6\n1.01   0.487              # [long[]]\xe2\x80\xa6\n1.02   0.492              # [int16[]]\xe2\x80\xa6\n1.08   0.521              # [decimal[]]\xe2\x80\xa6\n1.09   0.526              # [object[]]\xe2\x80\xa6\n1.11   0.533              # [double[]]\xe2\x80\xa6\n1.12   0.537              # [datetime[]]\xe2\x80\xa6\n\n============== 1D: SET (INCREMENT) performance (avg. of 15 runs)\n\nFactor Secs (15-run avg.) Command\n------ ------------------ -------\n1.00   0.526              # [double[]]\xe2\x80\xa6\n1.03   0.541              # [int[]] (Int32)\xe2\x80\xa6\n1.11   0.584              # [long[]]\xe2\x80\xa6\n1.30   0.681              # [byte[]]\xe2\x80\xa6\n1.40   0.738              # [int16[]]\xe2\x80\xa6\n1.57   0.826              # [decimal[]]\xe2\x80\xa6\n1.86   0.975              # [object[]], no casts\xe2\x80\xa6\n2.90   1.523              # [object[]] with [int] casts\xe2\x80\xa6\n7.34   3.857              # [datetime[]]\xe2\x80\xa6\n\n============== 2D: GET performance (avg. of 15 runs)\n\nFactor Secs (15-run avg.) Command\n------ ------------------ -------\n1.00   0.620              # [object[,]]\xe2\x80\xa6\n1.13   0.701              # [double[,]]\xe2\x80\xa6\n1.13   0.702              # [int[,]] (Int32)\xe2\x80\xa6\n1.18   0.731              # [datetime[,]]\xe2\x80\xa6\n1.20   0.747              # [long[,]]\xe2\x80\xa6\n1.23   0.764              # [byte[,]]\xe2\x80\xa6\n1.26   0.782              # [decimal[,]]\xe2\x80\xa6\n1.34   0.828              # [int16[,]]\xe2\x80\xa6\n\n============== 2D: SET (INCREMENT) performance (avg. of 15 runs)\n\nFactor Secs (15-run avg.) Command\n------ ------------------ -------\n1.00   0.891              # [double[,]]\xe2\x80\xa6\n1.02   0.904              # [long[,]]\xe2\x80\xa6\n1.10   0.977              # [byte[,]]\xe2\x80\xa6\n1.24   1.107              # [object[,]], no casts\xe2\x80\xa6\n1.27   1.131              # [int16[,]]\xe2\x80\xa6\n1.28   1.143              # [int[,]] (Int32)\xe2\x80\xa6\n1.46   1.300              # [decimal[,]]\xe2\x80\xa6\n1.72   1.530              # [object[,]] with [int] casts\xe2\x80\xa6\n5.25   4.679              # [datetime[,]]\xe2\x80\xa6\n
Run Code Online (Sandbox Code Playgroud)\n

基准源代码

\n
$runs = 15 # how many run to average.\n$d1 = $d2 = 1000  # 1000 x 1000 2D array\n$d = $d1 * $d2    # 1 million-element 1D array\n\n# index arrays for looping\n$indices_d1 = 0..($d1 - 1)\n$indices_d2 = 0..($d2 - 1)\n$indices = 0..($d - 1)\n\n# 1D arrays.\n$ao = [object[]]::new($d)\n$ab = [byte[]]::new($d)\n$ai16 = [int16[]]::new($d)\n$ai = [int[]]::new($d)\n$al = [long[]]::new($d)\n$adec = [decimal[]]::new($d)\n$ad = [double[]]::new($d)\n$adt = [datetime[]]::new($d)\n\n# 2D arrays.\n$ao_2d = [object[,]]::new($d1, $d2)\n$ab_2d = [byte[,]]::new($d1, $d2)\n$ai16_2d = [int16[,]]::new($d1, $d2)\n$ai_2d = [int[,]]::new($d1, $d2)\n$al_2d = [long[,]]::new($d1, $d2)\n$adec_2d = [decimal[,]]::new($d1, $d2)\n$ad_2d = [double[,]]::new($d1, $d2)\n$adt_2d = [datetime[,]]::new($d1, $d2)\n\n"`n============== 1D: GET performance (avg. of $runs runs)"\n\nTime-Command -Count $runs `\n{ # [object[]]\n  foreach ($i in $indices) { $null = $ao[$i] }\n},\n{ # [byte[]]\n  foreach ($i in $indices) { $null = $ab[$i] }\n}, \n{ # [int16[]]\n  foreach ($i in $indices) { $null = $ai16[$i] }\n},\n{ # [int[]] (Int32)\n  foreach ($i in $indices) { $null = $ai[$i] }\n},\n{ # [long[]]\n  foreach ($i in $indices) { $null = $al[$i] }\n}, \n{ # [decimal[]]\n  foreach ($i in $indices) { $null = $adec[$i] }\n}, \n{ # [double[]]\n  foreach ($i in $indices) { $null = $ad[$i] }\n},\n{ # [datetime[]]\n  foreach ($i in $indices) { $null = $adt[$i] }\n} | Format-Table Factor, Secs*, Command\n\n"============== 1D: SET (INCREMENT) performance (avg. of $runs runs)"\n\nTime-Command -Count $runs `\n{ # [object[]], no casts\n  foreach ($i in $indices) { ++$ao[$i] }\n},\n{ # [object[]] with [int] casts\n  foreach ($i in $indices) { $ao[$i] = [int] ($ao[$i] + 1) }\n},\n{ # [byte[]]\n  foreach ($i in $indices) { ++$ab[$i] }\n}, \n{ # [int16[]]\n  foreach ($i in $indices) { ++$ai16[$i] }\n},\n{ # [int[]] (Int32)\n  foreach ($i in $indices) { ++$ai[$i] }\n},\n{ # [long[]]\n  foreach ($i in $indices) { ++$al[$i] }\n},\n{ # [decimal[]]\n  foreach ($i in $indices) { ++$adec[$i] }\n},\n{ # [double[]]\n  foreach ($i in $indices) { ++$ad[$i] }\n},\n{ # [datetime[]]\n  foreach ($i in $indices) { $adt[$i] += 1 } # ++ not supported\n} | Format-Table Factor, Secs*, Command\n\n"============== 2D: GET performance (avg. of $runs runs)"\n\nTime-Command -Count $runs `\n{ # [object[,]]\n  foreach ($i in $indices_d1) {\n    foreach ($j in $indices_d2) { $null = $ao_2d[$i, $j] }\n  }\n},\n{ # [byte[,]]\n  foreach ($i in $indices_d1) {\n    foreach ($j in $indices_d2) { $null = $ab_2d[$i, $j] } \n  }\n},\n{ # [int16[,]]\n  foreach ($i in $indices_d1) {\n    foreach ($j in $indices_d2) { $null = $ai16_2d[$i, $j] }\n  }\n}, \n{ # [int[,]] (Int32)\n  foreach ($i in $indices_d1) {\n    foreach ($j in $indices_d2) { $null = $ai_2d[$i, $j] }\n  }\n},\n{ # [long[,]]\n  foreach ($i in $indices_d1) {\n    foreach ($j in $indices_d2) { $null = $al_2d[$i, $j] } \n  }\n}, \n{ # [decimal[,]]\n  foreach ($i in $indices_d1) {\n    foreach ($j in $indices_d2) { $null = $adec_2d[$i, $j] } \n  }\n},\n{ # [double[,]]\n  foreach ($i in $indices_d1) {\n    foreach ($j in $indices_d2) { $null = $ad_2d[$i, $j] } \n  }\n}, \n{ # [datetime[,]]\n  foreach ($i in $indices_d1) {\n    foreach ($j in $indices_d2) { $null = $adt_2d[$i, $j] } \n  }\n} | Format-Table Factor, Secs*, Command\n\n"============== 2D: SET (INCREMENT) performance (avg. of $runs runs)"\n\nTime-Command -Count $runs `\n{ # [object[,]], no casts\n  foreach ($i in $indices_d1) {\n    foreach ($j in $indices_d2) { ++$ao_2d[$i, $j] }\n  }\n},\n{ # [object[,]] with [int] casts\n  foreach ($i in $indices_d1) {\n    foreach ($j in $indices_d2) { $ao_2d[$i, $j] = [int] ($ao_2d[$i, $j] + 1) }\n  }\n}, \n{ # [byte[,]]\n  foreach ($i in $indices_d1) {\n    foreach ($j in $indices_d2) { ++$ab_2d[$i, $j] }\n  }\n}, \n{ # [int16[,]]\n  foreach ($i in $indices_d1) {\n    foreach ($j in $indices_d2) { ++$ai16_2d[$i, $j] }\n  }\n},\n{ # [int[,]] (Int32)\n  foreach ($i in $indices_d1) {\n    foreach ($j in $indices_d2) { ++$ai_2d[$i, $j] }\n  }\n},\n{ # [long[,]]\n  foreach ($i in $indices_d1) {\n    foreach ($j in $indices_d2) { ++$al_2d[$i, $j] }\n  }\n}, \n{ # [decimal[,]]\n  foreach ($i in $indices_d1) {\n    foreach ($j in $indices_d2) { ++$adec_2d[$i, $j] } \n  }\n},\n{ # [double[,]]\n  foreach ($i in $indices_d1) {\n    foreach ($j in $indices_d2) { ++$ad_2d[$i, $j] } \n  }\n},\n{ # [datetime[,]]\n  foreach ($i in $indices_d1) {\n    foreach ($j in $indices_d2) { $adt_2d[$i, $j] += 1 } # ++ not supported\n  }\n} | Format-Table Factor, Secs*, Command\n
Run Code Online (Sandbox Code Playgroud)\n
\n

[1] 原因是类型约束需要将类型转换属性附加到 PowerShell 变量对象。

\n