Wednesday, March 25, 2009

An Elegant Way to Parse C# Enums

If you're like me, you find the standard .NET way to parse Enums pretty awkward:

string aStr = "Truncate";
FileInfo aFN = (FileInfo) Enum.Parse(
typeof(FileInfo), aStr);

First, we have to provide the type of the enumeration twice. Second, I personally hate nested parentheses. I just cannot read the code with too many parentheses.

What I suggest is a thin wrapper:

public static class EnumParser<tEnum>
where tEnum : struct
{
public static tEnum Parse(string theVal)
{
return (tEnum)Enum.Parse(
typeof(tEnum), theVal);
}

public static tEnum Parse(string theVal, tEnum theDef)
{
try { return Parse(theVal); }
catch (ArgumentException) { return theDef; }
}
}

After several years of programming in C# I found I constantly use the exact same approach in most or all of my projects so I added it to my small C# toolkit.

The initial example would look more elegant with this new class in scope:

string aStr = "Truncate";
FileInfo aFN = EnumParser<FileInfo>.Parse(aStr);

The second overload allows us to use a default value:

string aStr = "Unknown";
FileInfo aFN = EnumParser<FileInfo>.Parse(
aStr, FileInfo.Truncate);

The provided solution is good but not perfect. Let's create another version of the EnumParser class:

public static class EnumParser
{
public static tEnum Parse<tEnum>(
string theVal) where tEnum : struct
{
return (tEnum)Enum.Parse(
typeof(tEnum), theVal);
}

public static tEnum Parse<tEnum>(
string theVal, tEnum theDef) where tEnum : struct
{
try { return Parse<tEnum>(theVal); }
catch (ArgumentException) { return theDef; }
}
}

As you see, we moved from a generic type to a non-generic type with two generic methods. Now let's look at the usage:

string aStr = "Truncate";
FileInfo aFN = EnumParser.Parse<FileInfo>(aStr);

The usage example when we don't specify a default value is almost identical to that of the generic type. However, things change significantly when a default value comes into play:

string aStr = "Unknown";
FileInfo aFN = EnumParser.Parse(aStr, FileInfo.Truncate);

As you see, we don't even specify the generic method arguments and the code will still compile successfully. The reason is: the compiler can resolve the arguments of the generic method from the argument that we specified. Our default value argument has now two roles: first, it is the value that will be returned in case the string is not recognized; second, it is an implicit type specifier for a generic method.

This is, of course, my favorite way to parse enums.

Cheers,
Kirill

3 comments:

  1. Nice pattern. Will definitely use it next time.

    ReplyDelete
  2. I was not able to understand what this article was talking about, but this sounds interesting. I'm also learning C# & had just stepped-in. Plz forgive my lack of understanding, I just had a doubt....
    I have Windows form form1 & added form2. I wanted to use form2 as a message window which will disappear after some interval of time. (just like a popup)

    Now how to do that? Can u use some simple way to make me understand that? sample code is always a wonderful help. :)

    ReplyDelete