Reputation: 1524
It looks like there are different kinds of hashtables in PowerShell, ones that are case sensitive and ones that are not. When defining a hashtable as the following it's not case sensitive:
$ht = @{ "Test" = "HI" }
$ht.Contains("test") #returns true, even with key name lowercase
True
$ht.ContainsKey("test") #returns true, even with key name lowercase
True
$ht.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Hashtable System.Object
However, if you define it as such it is case sensitive:
$ht_caseSensitive = New-Object System.Collections.Hashtable
$ht_caseSensitive.Add("Test", "HI")
$ht_caseSensitive.Contains("test") # returns false, since it's all lowercase
False
$ht_caseSensitive.ContainsKey("test") # returns false, same with contains key function
False
$ht_caseSensitive.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Hashtable System.Object
However, as shown in the GetType()
output, I can't seem to find a way to differentiate between these objects.
Upvotes: 4
Views: 520
Reputation: 439247
Why is one case sensitive and the other is not?
Because PowerShell often intentionally modifies default .NET functionality to be case-insensitive, in keeping with PowerShell being case-insensitive overall.
Therefore, while hashtable literal @{ "Test" = "HI" }
indeed creates a [hashtable]
(System.Collections.Hashtable
) instance, PowerShell constructs it with an explicitly case-INSENSITIVE equality comparer behind the scenes.[1]
By contrast, constructing a [hashtable]
instance directly honors .NET's defaults, which is to simply call the .Equals()
method for key lookups, which in the case of string keys (the typical case) amounts to ordinal, case-SENSITIVE comparison.
Is there any way I can differentiate between them when it's important to know whether I'm dealing with a hashtable that's case sensitive or not?
If you always use PowerShell hashtable literals and / or construct (initialize) hashtables via literal @{}
, you'll always get case-insensitive lookups.
In case you still need to reflect on a given [hashtable]
instance to determine whether its equality comparer is case-sensitive or not, you can use the following - obscure - technique:
# Create a case-SENSITIVE hashtable.
# Note: in PowerShell v5+, the more efficient alternative is:
# $someHashTable = [hashtable]::new()
$someHashTable = New-Object hashtable # System.Collections.Hashtable
# Use reflection to get the *non-public* (protected, in this case)
# .EqualityComparer property value.
$equalityComparer = [hashtable].GetProperty(
'EqualityComparer',
[System.Reflection.BindingFlags]'NonPublic, Instance'
).GetValue($someHashTable)
# The hashtable is case-sensitive if it either doesn't define
# an explicit comparer or uses one that is case-sensitive.
$isCaseSensitive = $null -eq $equalityComparer -or
0 -ne $equalityComparer.Compare('a', 'A')
[1] In Windows PowerShell (versions up to v5.1), this comparer was - surprisingly - culture-sensitive; in PowerShell [Core] as of v7.0, it uses ordinal (but still case-insensitive) comparison. Note that in many other contexts PowerShell uses culture-invariant behavior instead - see this answer.
Upvotes: 5