Frustration with the StringDictionary class.

Today I ran into an annoying nuance of the StringDictionary class (located in the System.Collections.Specialized namespace so as not to be confused with the other imposter string dictionaries). It's not a bug, but I feel the API for it could have been slightly better on this minor point.

Right there in the documentation for the class it points out that:

The key is handled in a case-insensitive manner; it is translated to lower case before it is used with the string dictionary.

Thus it takes the key you give it, and changes it to a lower case value before storing it. I verified this with Reflector.

public virtual void Add(string key, string value)
{
      this.contents.Add(key.ToLower(CultureInfo.InvariantCulture), value);
}

Whoa there! Stop the presses! Call Michael Moore! Ok, ok. So in the grand scheme of things, it's a minor issue. It's not a huge deal. However, API usability (and language design) focuses on keeping the minor annoyances to a minimum. Otherwise they'll add up and form a major disturbance and soon you'll have an angry developer standing on his/her soap box whining and ranting about it on a blog.

The issue for me is that this wasn't the behavior I expected. If an API is going to change data you give it, I wish it would indicate that somehow. Perhaps the method could be AddKeyAndValueButLowerCaseTheKey() ;). All joking aside, it isn't necessary to lower-case the key before storing it in order to provide case-insensitive storage. Internally, StringDictionary uses a HashTable to store its keys and values. The dictionary could have made the HashTable use a CaseInsensitiveHashCodeProvider when storing keys. That way the key doesn't have to change, but you get case insensitivity.

In any case, like I said, it's normally not a big deal except for the fact that I am building a Setup and Deployment project within Visual Studio.NET for a Windows service. When building a project installer, the Installer (in the namespace System.Configuration.Install) class provides a property called Context of type InstallContext. This class gives you a property called Parameters which is a StringDictionary containing values you may have pulled from the user interface.

I'm trying to look up nodes within an XML file that correspond to the keys of the Parameters dictionary using XPath, hoping to update the node values with the values from the dictionary. However, XPath is case sensitive so I can't match these things up. Unfortunately I can't change the XML to be all lower case, and I can't change the Installer class to not use a StringDictionary (or better yet, correct the dictionary's behavior), so I'm stuck with resorting to a hack until something better comes along.

What others have said

Requesting Gravatar... Dare Obasanjo Jun 25, 2004 8:28 AM
# re: Frustration with the StringDictionary class.
Use EXSLT.NET, it comes with handy functions for doing upper & lower casing of text in XPath.
Requesting Gravatar... Haacked Jun 25, 2004 9:13 AM
# re: Frustration with the StringDictionary class.
Thanks, I'll check it out.
Requesting Gravatar... Brian Sep 18, 2006 2:38 PM
# re: Frustration with the StringDictionary class.
Damn...you confirmed what I thought might be the case. Unfortunately, the case is very important to what I'm doing. Damn! I was using a regular dictionary, but I gather sometimes as many as 1,000,000 entries and don't want to use a database. Damn! Thanks for the blog.
Requesting Gravatar... car insurance texas Oct 18, 2006 2:13 PM
# car insurance texas
Houdini stifling Jura sheeting duplicators,Wilkinson bestseller?<A HREF="http://insurance-jobs.extremetravelinsurance.com/">insurance jobs</A> http://insurance-jobs.extremetravelinsurance.com/
Requesting Gravatar... Pirmin Jul 29, 2007 11:34 PM
# re: Frustration with the StringDictionary class.
I had the same problem!
Instead of StringDictionary, now I'm using
Dictionary<string, string> (System.Collections.Generic).
This works fine for me.
Requesting Gravatar... Randy F Oct 17, 2007 4:16 AM
# re: Frustration with the StringDictionary class.
I was trying to do a similar operation with storing an XPath string in the DictionaryEntry.Key so I could query an XML document and set the DictionaryEntry.Value to the value of the node returned. Thanks for doing all the research.

My simple fix was to refactor using the Dictionary<String, String> generic as advised by Pirmin. This has had almost no impact on existing code. Any foreach loops I simply changed DictionaryEntry to KeyValuePair<String, String>. Now the key I inserted is the key retrieved.

I agree with you that this hidden mutation of the key on the Add method is unnecessary and frustrating.

Thanks Phil and Pirmin.
Requesting Gravatar... Simian Feb 08, 2009 10:34 PM
# re: Frustration with the StringDictionary class.
I had the same problem, I thought the StringDictionary would be perfect for my needs and was confused when I first used it. Looking at it carefully I had to revert back to using a Dictionary<String, String>
Requesting Gravatar... Ursine Jan 07, 2010 8:29 AM
# re: Frustration with the StringDictionary class.
Yeah, same thing happened to me. No big deal, just unexpected so I wasted a little time.
Requesting Gravatar... shaun Sep 14, 2011 2:31 PM
# re: Frustration with the StringDictionary class.
Interestingly it can also ignore the INDEX you place the item in, it has no AddAt or Insert capability. Had an odd situation using StringDictionary trying to maintain an order of fields, it would just ignore the order I Added in, where as Generic<String,String> maintained it. I was attaching it in an ASP.NET ViewState so not sure if that had anything to do with it.
Requesting Gravatar... Peter Dec 07, 2011 3:44 AM
# re: Frustration with the StringDictionary class.
On a somewhat related note, I also found that the StringDictionary resorted the entries that I added into it. It didn't do it alphabetically or anything, but the entries were in a somewhat random order when I looped through them afterwards.

Changing the code to use a Dictionary<string,string> solved that problem as well.

What do you have to say?

(will show your gravatar)
Please add 8 and 5 and type the answer here: