Frustration with the StringDictionary class.

0 comments suggest edit

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.

Found a typo or error? Suggest an edit! If accepted, your contribution is listed automatically here.

Comments

avatar

12 responses

  1. Avatar for Dare Obasanjo
    Dare Obasanjo June 25th, 2004

    Use EXSLT.NET, it comes with handy functions for doing upper & lower casing of text in XPath.

  2. Avatar for Haacked
    Haacked June 25th, 2004

    Thanks, I'll check it out.

  3. Avatar for Brian
    Brian September 18th, 2006

    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.

  4. Avatar for car insurance texas
    car insurance texas October 18th, 2006

    Houdini stifling Jura sheeting duplicators,Wilkinson bestseller?<A HREF="http://insurance-jobs.extre... jobs</A> http://insurance-jobs.extre...

  5. Avatar for Pirmin
    Pirmin July 29th, 2007

    I had the same problem!
    Instead of StringDictionary, now I'm using
    Dictionary<string, string> (System.Collections.Generic).
    This works fine for me.

  6. Avatar for Randy F
    Randy F October 17th, 2007

    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.

  7. Avatar for Simian
    Simian February 8th, 2009

    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>

  8. Avatar for Ursine
    Ursine January 7th, 2010

    Yeah, same thing happened to me. No big deal, just unexpected so I wasted a little time.

  9. Avatar for shaun
    shaun September 14th, 2011

    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.

  10. Avatar for Peter
    Peter December 7th, 2011

    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.

  11. Avatar for Ruchitrivei19
    Ruchitrivei19 July 27th, 2012

    Thanks for figuring out, I am also struglling there and now will use Dictionary

  12. Avatar for IT Training
    IT Training February 23rd, 2013

    Yes, it is true.