I'm building a function to extend the Enum.Parse
concept that
- Allows a default value to be parsed in case that an Enum value is not found
- Is case insensitive
So I wrote the following:
public static T GetEnumFromString<T>(string value, T defaultValue) where T : Enum
{
if (string.IsNullOrEmpty(value)) return defaultValue;
foreach (T item in Enum.GetValues(typeof(T)))
{
if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
}
return defaultValue;
}
I am getting a Error Constraint cannot be special class System.Enum
.
Fair enough, but is there a workaround to allow a Generic Enum, or am I going to have to mimic the Parse
function and pass a type as an attribute, which forces the ugly boxing requirement to your code.
EDIT All suggestions below have been greatly appreciated, thanks.
Have settled on (I've left the loop to maintain case insensitivity - I am using this when parsing XML)
public static class EnumUtils
{
public static T ParseEnum<T>(string value, T defaultValue) where T : struct, IConvertible
{
if (!typeof(T).IsEnum) throw new ArgumentException("T must be an enumerated type");
if (string.IsNullOrEmpty(value)) return defaultValue;
foreach (T item in Enum.GetValues(typeof(T)))
{
if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
}
return defaultValue;
}
}
EDIT: (16th Feb 2015) Julien Lebosquain has recently posted a compiler enforced type-safe generic solution in MSIL or F# below, which is well worth a look, and an upvote. I will remove this edit if the solution bubbles further up the page.
In java, you would use...
Pretty straightforward, that.
You can define a static constructor for the class that will check that the type T is an enum and throw an exception if it is not. This is the method mentioned by Jeffery Richter in his book CLR via C#.
Then in the parse method, you can just use Enum.Parse(typeof(T), input, true) to convert from string to the enum. The last true parameter is for ignoring case of the input.
Hope this is helpful:
The existing answers are true as of C# <=7.2. However, there is a C# language feature request (tied to a corefx feature request) to allow the following;
At time of writing, the feature is "In discussion" at the Language Development Meetings.
EDIT
As per nawfal's info, this is being introduced in C# 7.3.
This feature is finally supported in C# 7.3!
The following snippet (from the dotnet samples) demonstrates it use:
Be sure to set your language version in your C# project to version 7.3.
Original Answer below:
I'm late to the game, but I took it as a challenge to see how it could be done. It's not possible in C# (or VB.NET, but scroll down for F#), but is possible in MSIL. I wrote this little....thing
Which generates a function that would look like this, if it were valid C#:
Then with the following C# code:
Unfortunately, this means having this part of your code written in MSIL instead of C#, with the only added benefit being that you're able to constrain this method by
System.Enum
. It's also kind of a bummer, because it gets compiled into a separate assembly. However, it doesn't mean you have to deploy it that way.By removing the line
.assembly MyThing{}
and invoking ilasm as follows:you get a netmodule instead of an assembly.
Unfortunately, VS2010 (and earlier, obviously) does not support adding netmodule references, which means you'd have to leave it in 2 separate assemblies when you're debugging. The only way you can add them as part of your assembly would be to run csc.exe yourself using the
/addmodule:{files}
command line argument. It wouldn't be too painful in an MSBuild script. Of course, if you're brave or stupid, you can run csc yourself manually each time. And it certainly gets more complicated as multiple assemblies need access to it.So, it CAN be done in .Net. Is it worth the extra effort? Um, well, I guess I'll let you decide on that one.
F# Solution as alternative
Extra Credit: It turns out that a generic restriction on
enum
is possible in at least one other .NET language besides MSIL: F#.This one is easier to maintain since it's a well-known language with full Visual Studio IDE support, but you still need a separate project in your solution for it. However, it naturally produces considerably different IL (the code is very different) and it relies on the
FSharp.Core
library, which, just like any other external library, needs to become part of your distribution.Here's how you can use it (basically the same as the MSIL solution), and to show that it correctly fails on otherwise synonymous structs:
I always liked this (you could modify as appropriate):