powershellreadonly-collection

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


I am familiar with creating read-only variables and constants in PowerShell by using the Set-Variable command with something like this:

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

Or alternatively to sub ReadOnly with Constant for non-removable variables.

I assumed that with the ReadOnly option I would not be able to modify the collection, but this isn't the case. For example, $STANDARD_TOKEN_PARAMS.someNewEntry = 'newEntry' is valid and modifies the collection accordingly.

Are there similar commands that I could use to create a real 'ReadOnly' collection in PowerShell?


Solution

  • The options ReadOnly and Constant are variable (data-holder) concepts: they only prevent assigning a new value to the variable, they don't prevent modification of the value that a read-only/constant variable immutably stores.

    To also prevent modification of the value (object) itself, you must additionally use a read-only data type[1] to store in the read-only/constant variable. To get a read-only hash table (dictionary), use the generic System.Collections.ObjectModel.ReadOnlyDictionary[TKey, TValue] type:

    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)
    )
    

    Note:


    [1] This isn't always necessary, namely not if the value is an instance of a by definition immutable .NET primitive type (a property-less type such as [int]) or a [string].

    [2] Note that [hashtable] in Windows PowerShell and (obsolete) PowerShell Core versions 6.1 and below use [System.StringComparer]::CurrentCultureIgnoreCase, i.e, culture-sensitive lookups instead - see this GitHub issue for background information.