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:
Unfortunately, you cannot directly initialize a ReadOnlyDictionary[TKey, TValue]
instance from a PowerShell [hashtable]
, because a generic IDictionary
-implementing instance with matching types is required; therefore, an auxiliary System.Collections.Generic.Dictionar[TKey, TValue]
instance is used.
Note the [System.StringComparer]::OrdinalIgnoreCase
argument passed to the aux. dictionary, which ensures that key lookups are case-insensitive, the way they are by default in PowerShell [hashtables]
.[2]
While the resulting $STANDARD_TOKEN_PARAMS
read-only variable's value is then effectively also read-only, accidental attempts to modify the read-only dictionary result in error messages that aren't obvious, as of PowerShell Core 7.1:
$STANDARD_TOKEN_PARAMS['Username'] = 'foo'
unhelpfully reports: Unable to index into an object of type "System.Collections.ObjectModel.ReadOnlyDictionary
2[System.String,System.String]`
$STANDARD_TOKEN_PARAMS.Add('foo', 'bar')
unhelpfully reports: Cannot find an overload for "Add" and the argument count: "2"
$STANDARD_TOKEN_PARAMS.Clear()
unhelpfully reports: Cannot find an overload for "Clear" and the argument count: "0".
- Only using dot notation provides a helpful error message:
$STANDARD_TOKEN_PARAMS.Username = 'foo'
reports Collection is read-only.
- GitHub issue #15118 proposes improving these error messages.
[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.