The Wiert Corner – irregular stream of stuff

Jeroen W. Pluimers on .NET, C#, Delphi, databases, and personal interests

  • My badges

  • Twitter Updates

  • My Flickr Stream

  • Pages

  • All categories

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 4,262 other subscribers

.NET Enums enumerated: System.ArgumentException was unhandled by user code Message=An item with the same key has already been added.

Posted by jpluimers on 2012/06/13

All .NET languages have a cool feature: enumerated types, for instance enum in C#Enum in VB.NET, or the enum in the  Oxygene language of Delphi Prism. You can even specify the underlying integral type.

It allows you to name values, which can make your code very readable.

It also allows you to assign an integer to each of those values, which allows you to map them to existing integers in the ‘real’ world. This is both a powerful and potentially uncool feature of enums (the other uncool feature is that though you can have bitflags in Enums, most .NET languages don’t have a Set type. You need Set Extensions to do things like Set operations on Card Suit bitflags using C# extension methods).

Because of my background in the 80s in MuSimp and Turbo Pascal, I’ve done quite a bit of enums and sets in the past, hence the mentioned C# enum extension methods mentioned above. While writing this article I also found out Extending’ the Enum Class to Validate the Enum Values with the Flags Attribute that mentions quite a bit of stuff that is complementary to my code and what you will see below.

The risk of assigning integer values on C# enum is that you can assign the same integer value to multiple enum elements.

For instance, this code will fail:

            Environment.SpecialFolder[] specialFolders = (Environment.SpecialFolder[])Enum.GetValues(typeof(Environment.SpecialFolder));

            Dictionary<string, string> specialFoldersDictionary = new Dictionary<string, string>();
            foreach (Environment.SpecialFolder specialFolder in specialFolders)
            {
                string specialFolderPath = Environment.GetFolderPath(specialFolder, Environment.SpecialFolderOption.None);
                string specialFolderString = specialFolder.ToString();
                specialFoldersDictionary.Add(specialFolderString, specialFolderPath);
            }

The failure is an exception like this:

System.ArgumentException was unhandled by user code
  Message=An item with the same key has already been added.
  Source=mscorlib
  StackTrace:
       at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
       at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
       at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)

The reason the code fails is that Environment.SpecialFolders looks like this: it assigns the integer value 5 to both the element Personal and the element MyDocuments:

public enum SpecialFolder
{
    Desktop = 0,
    Personal = 5,
    MyDocuments = 5,
    Windows = 0x24
}

Because of this, Enum.GetValues will return 5 twice, and this will both times cast to Environment.SpecialFolder.Personal. Inserting that into the Dictionary will give you an error.
In my case that is good, as I want both the name Personal and the name MyDocuments.

So in stead of Enum.GetValues, I need to call Enum.GetNames like the next code fragment, and cast from the string into the Enum in a convoluted way (you need the Environment.SpecialFolder type three times):

            string[] specialFolderNames = Enum.GetNames(typeof(Environment.SpecialFolder));

            Dictionary<string, string> specialFoldersDictionary = new Dictionary<string, string>();
            foreach (string specialFolderName in specialFolderNames)
            {
                Environment.SpecialFolder specialFolder = (Environment.SpecialFolder)Enum.Parse(typeof(Environment.SpecialFolder), specialFolderName);
                string specialFolderPath = Environment.GetFolderPath(specialFolder, Environment.SpecialFolderOption.None);
                specialFoldersDictionary.Add(specialFolderName, specialFolderPath);
            }

I’ve written a couple of generic C# code, and some C# extension methods that make this casting a lot easier.

Below is first the simplified code based on Enum.GetValues, which isn’t really nice because extension methods can only be run on instances (they can not be true static methods like class helpers in Delphi can):

            Environment.SpecialFolder dummy = Environment.SpecialFolder.AdminTools;
            List<Environment.SpecialFolder> specialFolders = dummy.ToList();

            Dictionary<string, string> specialFoldersDictionary = new Dictionary<string, string>();
            foreach (Environment.SpecialFolder specialFolder in specialFolders)
            {
                string specialFolderPath = Environment.GetFolderPath(specialFolder, Environment.SpecialFolderOption.None);
                string specialFolderString = specialFolder.ToString();
                specialFoldersDictionary.Add(specialFolderString, specialFolderPath);
            }

then the simplified code based on Enum.GetNames:

            string[] specialFolderNames = Enum.GetNames(typeof(Environment.SpecialFolder));

            Dictionary<string, string> specialFoldersDictionary = new Dictionary<string, string>();
            foreach (string specialFolderName in specialFolderNames)
            {
                Environment.SpecialFolder specialFolder = specialFolderName.Parse<Environment.SpecialFolder>();
                string specialFolderPath = Environment.GetFolderPath(specialFolder, Environment.SpecialFolderOption.None);
                specialFoldersDictionary.Add(specialFolderName, specialFolderPath);
            }

–jeroen

PS: If you like Turbo Pascal, you might like the TurboPascal.org site too.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.