是否可以在 PowerShell 中创建不可修改的哈希表?

Is it possible to create a non-modifiable hashtable in PowerShell?

我熟悉使用 Set-Variable 命令在 PowerShell 中创建只读变量和常量,如下所示:

Set-Variable -Option ReadOnly, AllScope -Name STANDARD_TOKEN_PARAMS -Force -Value @{
                Username = 'username'
                Password = 'password'
                ClientId = 'clientID'
            }

或者替代 ReadOnlyConstant 对于不可移动变量。

我假设使用 ReadOnly 选项我将无法修改集合,但事实并非如此。例如,$STANDARD_TOKEN_PARAMS.someNewEntry = 'newEntry' 有效并相应地修改集合。

是否有类似的命令可用于在 PowerShell 中创建真正的 'ReadOnly' 集合?

选项ReadOnlyConstant变量(数据持有者)概念:它们只阻止分配新值 到变量,它们不会阻止修改 ,read-only/constant 变量不可变地存储。

为了同时防止修改值(对象)本身,您必须另外使用只读的数据类型[1]存储在 read-only/constant 变量中。要获得只读散列 table(字典),请使用通用 System.Collections.ObjectModel.ReadOnlyDictionary[TKey, TValue] 类型:

Set-Variable -Option ReadOnly, AllScope -Name STANDARD_TOKEN_PARAMS -Value $(
    $dict = [System.Collections.Generic.Dictionary[string, string]]::new(
      [System.StringComparer]::OrdinalIgnoreCase
    )
    $dict.Add('Username', 'username')
    $dict.Add('Password', 'password')
    $dict.Add('ClientId', 'clientID')
    [System.Collections.ObjectModel.ReadOnlyDictionary[string, string]]::new($dict)
)

注:

  • 不幸的是,您不能直接从 PowerShell [hashtable] 初始化 ReadOnlyDictionary[TKey, TValue] 实例,因为 generic IDictionary - 需要 匹配类型 的实例;因此,使用辅助 System.Collections.Generic.Dictionar[TKey, TValue] 实例。

  • 注意传递给辅助的 [System.StringComparer]::OrdinalIgnoreCase 参数。字典,它确保键查找不区分大小写,就像它们在 PowerShell [hashtables].[2][ 中的默认方式一样=37=]

  • 虽然生成的 $STANDARD_TOKEN_PARAMS 只读变量的 value 实际上也是只读的,但意外尝试修改只读字典导致错误消息不明显,从 PowerShell Core 7.1 开始:

    • $STANDARD_TOKEN_PARAMS['Username'] = 'foo' 毫无帮助的报告:Unable to index into an object of type "System.Collections.ObjectModel.ReadOnlyDictionary2[System.String,System.String]`
    • $STANDARD_TOKEN_PARAMS.Add('foo', 'bar') 毫无帮助的报告:Cannot find an overload for "Add" and the argument count: "2"
    • $STANDARD_TOKEN_PARAMS.Clear() 毫无帮助的报告:Cannot find an overload for "Clear" and the argument count: "0".
    • Only using dot notation 提供有用的错误消息:$STANDARD_TOKEN_PARAMS.Username = 'foo' 报告 Collection is read-only.
    • GitHub issue #15118 建议改进这些错误消息。

[1] 这不是 always 必需的,也就是说,如果值是 a 根据定义的实例 immutable .NET 原始类型(属性-less 类型,例如 [int])或 [string].

[2] 请注意 [hashtable]Windows PowerShell 和(过时的)PowerShell Core 版本 6.1 及以下使用 [System.StringComparer]::CurrentCultureIgnoreCase,即 文化敏感 查找 - 请参阅 this GitHub issue 了解背景信息。