Search code examples
powershelldictionarycase-sensitive

Check whether a dictionary is case sensitive


I would like to check whether a given dictionary ($Dictionary -is [System.Collections.IDictionary]) is case sensitive?

  • For a [HashTable], it is described here:
    Distinguish different kinds of hashtables in PowerShell?
  • For a $Dictionary = [system.collections.generic.dictionary[string,object]]::new(), I might simply do: $Dictionary.Comparer.Equals('A', 'a')
  • But for e.g. an [Ordered] dictionary, I have no clue...

Is there a generic way to check the EqualityComparer of an dictionary?
Or: how do I cover all dictionary flavors?


Solution

  • As stated in comments, and answering your question:

    Is there a generic way to check the EqualityComparer of an dictionary?

    As far as I can tell, there is not, you can do your best to keep track of how to handle each specific case, some might be via reflection like with Hashtable or OrderedDictionary and some might be via a public property like with Dictionary<TKey, TValue> and other generic types but that covers only specific cases.

    I believe there is no way to cover all types implementing the IDictionary and IDictionary<TKey, TValue> interfaces. The reflection approaches are obviously not recommended, nobody is ensuring you that those private fields or properties will change in the future.

    Leaving that aside, below might help you get a better perspective on how you can approach this problem.

    $supportedGenericTypes = @(
        # there is no interface ensuring that a type implementing `IDictionary<TKey, TValue>`
        # will have a `.Comparer` property, you will need to manually check if they actually do
        # before adding them to this list
        [System.Collections.Generic.Dictionary`2]
        [System.Collections.Concurrent.ConcurrentDictionary`2]
        [System.Collections.Generic.SortedDictionary`2]
    )
    
    [System.Collections.Generic.Dictionary[string, string]]::new(),
    [ordered]@{},
    @{},
    [System.Collections.Concurrent.ConcurrentDictionary[string, string]]::new(),
    [System.Collections.Generic.SortedDictionary[string, string]]::new() |
        ForEach-Object {
            $type = $_.GetType()
            if ($type.IsGenericType -and $type.GetGenericTypeDefinition() -in $supportedGenericTypes) {
                $comparer = $_.Comparer
                # need to cover any custom type implementing `IDicitonary<TKey, TValue>` here...
            }
            elseif (-not $type.IsGenericType) {
                $instance = $_
    
                switch ($type) {
                    ([System.Collections.Hashtable]) {
                        $comparer = [System.Collections.Hashtable].
                            GetField('_keycomparer', [System.Reflection.BindingFlags] 'NonPublic,Instance').
                            GetValue($instance)
                    }
                    ([System.Collections.Specialized.OrderedDictionary]) {
                        $comparer = [System.Collections.Specialized.OrderedDictionary].
                            GetField('_comparer', [System.Reflection.BindingFlags] 'NonPublic,Instance').
                            GetValue($instance)
                    }
                    # need to cover any custom type implementing `IDicitonary` here...
                }
            }
            else {
                throw [System.NotImplementedException]::new()
            }
    
            [pscustomobject]@{
                Type     = $type
                Comparer = $comparer
            }
        }