有人可以解释哈希表的 Add_member 和 $hashTable.Something 之间的区别吗?

Can someone explain the difference between Add_member and $hashTable.Something for a hashtable?

假设我有一个哈希表:

$HashTable = @{}

现在,当我想向哈希表中添加一些内容时,我通常这样做:

$HashTable.Something = 'Something'

当我测试 $hashTable 时,它​​会显示这个输出:

$HashTable

Name                           Value                                                                                                                                     
----                           -----                                                                                                                                     
Something                      Something 

我的一个同事使用 Add-Member 代替:

$HashTable | Add-Member -NotePropertyName 'SomethingAddMember' -NotePropertyValue 'SomethingAddMember'

它有效,但是当你查看 $HashTable 时它仍然只会显示:

$HashTable

Name                           Value                                                                                                                                     
----                           -----                                                                                                                                     
Something                      Something

当我运行这个$HashTable.SomethingAddMember时,它会显示:

$HashTable.SomethingAddMember

为什么它没有出现在 HashTable 本身中?当我将它转换为 Json 时,它又出现了!这有多令人困惑,或者我错过了什么??

$HashTable | ConvertTo-Json
{
    "Something":  "Something",
    "SomethingAddMember":  "SomethingAddMember"
}

是不是因为Add-Member在HashTable中添加了一个新的属性而不是Keys with Values?怎么会这样?

@mathiasrjessen 已经解释过了。这里有一些命令可以显示它。

$Hash = @{'A' = 'now'; 'B' = 'then'}
$Hash
$Hash | Get-Member -MemberType Property,NoteProperty | Select-Object -Property Name,MemberType | Format-Table * -Force
$Hash.Something = 'Something'
$Hash
$Hash | Get-Member -MemberType Property,NoteProperty | Select-Object -Property Name,MemberType | Format-Table * -Force
$Hash | Add-Member -NotePropertyName 'SomethingAddMember' -NotePropertyValue 'SomethingAddMemberValue'
$Hash
$Hash.SomethingAddMember
$Hash | Get-Member -MemberType Property,NoteProperty | Select-Object -Property Name,MemberType | Format-Table * -Force

这里已经有很好的信息,但让我从概念上分解一下:

hashtable(键值对)的 条目 与其 .NET 类型的 成员(属性、方法、事件).

哈希表条目是 数据,可通过 System.Collections.Hashtable 类型的 成员 访问,特别是 .Keys.Values 属性和 parameterized .Item 属性,PowerShell(如 C#)通过 index 语法在语法上显示:

  • 也就是说,您可以通过在 [...] 中使用其键来获取条目的值,作为 .Item(...) 的语法糖;例如,
    @{ foo = 42 }['foo']@{ foo = 42 }.Item('foo')
  • 的快捷方式

此外,PowerShell 允许您使用点符号访问条目值,使用.member-access operator - 即使您访问的不是 成员 ,而是哈希表条目;例如
@{ foo = 42 }.foo 等同于 @{ foo = 42 }['foo'].

换句话说:

  • PowerShell 的 .(点符号)可以访问 两者:

  • 如果存在 名称冲突条目 访问优先 - 例如,@{ Count = 42 }.Count returns 42 而不是反映条目数的 type-native Count 属性 的值;要访问后者,请在 属性 名称前加上 get_ 并将其作为 方法 调用:@{ Count = 42 }.get_Count() returns 1, 条目数。

注意:Add-Member adds to an object aren't technically .NET type members (type-native properties and methods), but in PowerShell they act as such. (Such members, which are a feature of PowerShell's ETS (Extended Type System) 的成员仅由 PowerShell 知道,并且必须与它们关联的对象分开维护。)

换句话说:您的 Add-Member 调用添加了一个新的 属性 (NoteProperty) 而不是 entry 到哈希表实例,以及实例的类型原生属性,例如 .Count.Keys.Values.

关于 表面处理 条目与成员:

  • 由于哈希表的默认输出格式仅显示其 条目,因此您添加的任何 (ETS) 成员不会在输出中显示 - 您必须显式访问 .SomethingAddMember 属性;你也可以通过反射,使用Get-Member ($Hashtable | Get-Member SomethingAddMember)

    看到它
  • 虽然 ETS 属性通常很少出现,但它们在 序列化上下文中出现 ,例如使用 ConvertTo-Json,并且 在哈希表(字典)的情况下,您会得到 ETS 属性的 mixentries,也就是你看到的:

    • 在许多情况下,PowerShell 将哈希表(字典)视为常规对象,其中哈希表的 条目 - 而不是哈希表对象的真实类型原生属性 - 使用 代替属性 .

    • 但是,ETS属性仍然包含在内,[2]导致上述混合。


[1] 奇怪的是,如果 key 类型恰好是 [object],虽然这不是典型的,因为 generic type/interface 的重点是使用 specific 类型 - 请参阅GitHub issue #15997.

[2] 从 PowerShell 7.2 开始,ConvertTo-Json 不再为 [datetime][string] 实例序列化 ETS 属性(参见 GitHub PR #15665), but other serialization cmdlets such as ConvertTo-Csv 仍然如此。
在相关说明中,从 PowerShell 7.2 开始,当 ConvertTo-Json 确实包含 ETS 属性时,如果 ETS 属性 恰好 override 原生类型 - 见 GitHub issue #13998.