如果属性名称是整数,为什么我不能点引用哈希表键的值属性

Ben*_*est 5 powershell powershell-core

考虑以下哈希表:

$table = @{
  6           = '10.11.12.13', '10.11.12.14'
  15          = 'domain.tld'
  NameServers = '10.11.12.13', '10.11.12.14'
}
Run Code Online (Sandbox Code Playgroud)

通常,当您有哈希表时,您可以引用.键名称以更加面向对象的方式使用它,而不是使用数组访问器语法[key]。例如,调用NameServers上面哈希表上的键并.返回值,我可以按预期使用该属性值的成员:

$table.NameServers       # ========> 10.11.12.13
                         # ========> 10.11.12.14

$table.NameServers.Count # ========> 2
Run Code Online (Sandbox Code Playgroud)

但是,如果我尝试使用包含相同字符串内容的6a 访问键,它会引用正确的值,但我无法调用返回对象上的任何成员。.必须在这里使用传统的数组访问器:

$table.6        # =====> 10.11.12.13
                # =====> 10.11.12.14

$table.6.Count  # =====> ParserError:
                # =====> Line |
                # =====>    1 |  $hi.6.Count
                # =====>      |      ~
                # =====>      | Missing property name after reference operator.

$table[6].Count # =====> 2
Run Code Online (Sandbox Code Playgroud)

当然,( $table.6 ).Count可以解决这个问题,但这仍然是语法的一个奇怪的怪癖,我无法解释。有趣的是,将哈希表转换为 aPSCustomObject会产生同样的问题。这可能是一个解析器错误吗?还是还有其他事情发生?

我在 Windows PowerShell 和 PowerShell 7.1 中测试了这一点,并且在两者中都会发生这种情况。

mkl*_*nt0 6

注意:这个答案最初错误地声称不起作用$table.6,因为哈希表键是整数类型的,但它工作得很好,因为“属性”访问6中的.6也被解析为整数。

正如Gyula Kokas 的有用答案所指出的那样,您在 PowerShell 的解析器中看到了有问题的行为,从 PowerShell 7.2 开始,在GitHub 问题 #14036中详细讨论了这一问题。该行为哈希表 本身无关:

  • 接下来的.成员访问运算符如果看起来像数字文字,例如, (a ) 甚至(!, a ),则被解析为数字66l[long]6.0[double]

    • 请参阅下文了解如何使用该数字。
  • 任何使用其他属性访问的尝试都会触发您看到的错误:

    $foo.6.bar # !! Error "Missing property name after reference operator."
    
    Run Code Online (Sandbox Code Playgroud)

顺便说一句:PowerShell 甚至允许您使用变量引用,甚至表达式(包含在 中(...))作为属性名称(例如,$propName = 'Length'; 'foo'.$propName或 ('foo'.('L' + 'ength')

解决方法

  • 正如您所演示的,将第一个属性访问包含在(...)-中($table.6).Count是可行的,也是如此$table.(6).Count

  • 您还演示了$table[6].Count它适用于哈希表

  • 对于访问对象的实际属性$obj.'6'.Count也可以工作(假设属性名称始终是字符串),就像使用带有字符串键的哈希表一样(例如@{ '6'= 'six' }.'6'.Length


哈希表特定的注意事项:

为了方便语法,PowerShell 还允许使用属性访问语法 ( $obj.someProperty) 来访问哈希表的条目,在这种情况下,属性“名称”将被视为哈希表中条目的键。

访问哈希表条目的类型本机语法是$hash[$someKey]索引语法( )

虽然属性名称(指.NET 类型的成员)始终是字符串

  • 哈希表的可以是任何类型
  • 访问条目时,查找键不仅必须具有正确的值,而且还必须与哈希表中存储的键具有完全相同的类型

也许令人惊讶的是,定义哈希表时,不带引号的有时会变成字符串数字

  • 如果未加引号的单词可以解释为数字文字(例如,6),则它会被如此解析,并且最终会得到一个数字键;在 的情况下6,它将是[int]-typed key,因为通常的数字文字类型适用(例如,1.0将成为一个[double]键(不建议,因为二进制浮点值并不总是具有精确的十进制表示形式),并且2147483648将成为[long])。

  • 否则(例如,NameServers),密钥将被[string]键入。

    • 警告:如果名称以数字开头(但不是数字文字;例如6a),则会发生错误;使用引用 ( '6a') 作为解决方法 - 请参阅GitHub 问题 #15925

也就是说,即使表达式中的字符串通常需要引号,但为了方便起见,您可以在哈希表文字中定义键时省略引号 - 但识别数字文字的规则仍然适用。

显式输入哈希表键:

  • 为了确保给定的键被解释为 a [string]请引用它(例如,'6'

  • 通常,您还可以使用强制转换来显式键入密钥。(eg [long] 6) 或者,在数字文字中,通常的数字类型后缀字符,例如Lfor [long](eg 6L) - 请参阅此答案以了解支持的后缀的概述,其数量在 PowerShell (Core) 7+ 中显着增长。

一个基于您的哈希表的示例:

从上面可以看出,您的6键是类型(如果您希望它是string[int] ,则必须按照哈希表文字中的方式定义)。'6'

因为6in$name.6被解析为 an ,所以查找会成功,但请注意,如果使用不同的数字类型,则查找不会成功:[int]

# !! Output is $null, because the entry key is of type [long], 
# !! whereas the lookup key is [int].
@{ 6L = '[long] 6' }.6
Run Code Online (Sandbox Code Playgroud)

财产访问的具体注意事项

通过实际的属性访问(访问 .NET 类型的本机成员),看起来像数字的名称实际上首先被解析为数字(之前必须转换为字符串),这可能会导致令人惊讶的行为

# !! Output is $null, because the 6L after "." is parsed as a 
# !! ([long]) number first, and then converted to a string, which
# !! results in "6" being used.
([pscustomobject] @{ '6L' = 'foo' }).6L

# OK - The quoting forces 6L to be a string.
([pscustomobject] @{ '6L' = 'foo' }).'6L' # -> 'foo'
Run Code Online (Sandbox Code Playgroud)