Case-Insensitive dictionary not working as expected

Viewed 52

I am caching some data in a dictionary from the OsiSoft "PI" system, indexing it by string keys. The keys come from user-input in a different system, and there is a mix of upper and lower case. the OsiSoft system keys are case-insensitive, so I need my dictionary to be case insensitive, too. However, the dictionary is not working as expected.

The dictionary is defined as follows:

Dictionary<string, PIPoint> PointsDictionary = new Dictionary<string, PIPoint>(StringComparer.CurrentCultureIgnoreCase);

It is populated from a structure returned from an OsiSoft structure that derives from IList

PointsDictionary = RegisteredPoints.ToDictionary(x => x.Name, x => x);

When I try to return the PIPoint value from the dictionary, it is not working as expected. I want to pass the Key in lower, upper or mixed case, and get the same value.

    public PIPoint GetPoint(string tag)
    {
        //sfiy-1401a/c6
        //SFIY-1401A/C6
        Debug.WriteLine("sfiy-1401a/c6" + ": " + PointsDictionary.ContainsKey("sfiy-1401a/c6"));
        Debug.WriteLine("SFIY-1401A/C6" + ": " + PointsDictionary.ContainsKey("SFIY-1401A/C6"));
        Debug.WriteLine("Match?" + ": " + "SFIY-1401A/C6".Equals("sfiy-1401a/c6", StringComparison.CurrentCultureIgnoreCase));
        if (tag == null || !PointsDictionary.ContainsKey(tag)) return null;
        return PointsDictionary[tag];
    }

Output from Debugger when running the above:

sfiy-1401a/c6: False

SFIY-1401A/C6: True

Match?: True

Am I fundamentally misunderstanding how the case-insensitive dictionary works, or is there something in the way I am populating it (converting IList.ToDictionary()) that means it is not working as intended?

1 Answers

The dictionary is defined as case-insensitive, but then you are overwriting it with a case-sensitive one.

// definition 
Dictionary<string, PIPoint> PointsDictionary = new Dictionary<string, PIPoint>(StringComparer.CurrentCultureIgnoreCase);

// later *reassignment*
PointsDictionary = RegisteredPoints.ToDictionary(x => x.Name, x => x);

You see, you're replacing the value! When you overwrite the value, the original comparer is lost. You need to specify the comparer again when you call ToDictionary. Luckily there is an overload that takes a key comparer:

PointsDictionary = RegisteredPoints.ToDictionary(x => x.Name, x => x, StringComparer.CurrentCultureIgnoreCase);

Note there are also overloads of ToDictionary that take only the key selector (and comparer), so you can simplify to:

PointsDictionary = RegisteredPoints.ToDictionary(x => x.Name, StringComparer.CurrentCultureIgnoreCase);

You can even pass the original Comparer so you don't have to remember exactly what type it was:

PointsDictionary = RegisteredPoints.ToDictionary(x => x.Name, PointsDictionary.Comparer);

Alternatively, you could clear the dictionary and the re-add all the items to it which will preserve the original comparer:

PointsDictionary.Clear():
foreach(var p in RegisteredPoints) 
   PointsDictionary[p.Name] = p:
Related