By johnc


2008-09-17 01:56:19 8 Comments

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.

21 comments

@DiskJunky 2018-03-22 15:27:56

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;

public class MyGeneric<TEnum> where TEnum : System.Enum
{ }

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.

EDIT 2

This is now in C# 7.3 forward (release notes)

Sample;

public static Dictionary<int, string> EnumNamedValues<T>()
    where T : System.Enum
{
    var result = new Dictionary<int, string>();
    var values = Enum.GetValues(typeof(T));

    foreach (int item in values)
        result.Add(item, Enum.GetName(typeof(T), item));
    return result;
}

@johnc 2018-03-27 20:59:46

Interesting discussion there, thanks. Nothing set in stone yet though (as yet)

@DiskJunky 2018-03-27 21:00:26

@johnc, very true but worth a note and it is a frequently asked feature. Fair odds on it coming in.

@nawfal 2018-04-11 17:46:26

@Pac0 2020-06-09 12:59:39

Please upvote this answer, it should be much much higher in the list here, now that the feature exists! :)

@Cubelaster 2019-11-06 13:29:48

This is my implementation. Basically, you can setup any attribute and it works.

public static class EnumExtensions
    {
        public static string GetDescription(this Enum @enum)
        {
            Type type = @enum.GetType();
            FieldInfo fi = type.GetField(@enum.ToString());
            DescriptionAttribute[] attrs =
                fi.GetCustomAttributes(typeof(DescriptionAttribute), false) as DescriptionAttribute[];
            if (attrs.Length > 0)
            {
                return attrs[0].Description;
            }
            return null;
        }
    }

@Julien Lebosquain 2015-02-15 15:16:07

C# ≥ 7.3

Starting with C# 7.3 (available with Visual Studio 2017 ≥ v15.7), this code is now completely valid:

public static TEnum Parse<TEnum>(string value)
    where TEnum : struct, Enum
{
 ...
}

C# ≤ 7.2

You can have a real compiler enforced enum constraint by abusing constraint inheritance. The following code specifies both a class and a struct constraints at the same time:

public abstract class EnumClassUtils<TClass>
where TClass : class
{

    public static TEnum Parse<TEnum>(string value)
    where TEnum : struct, TClass
    {
        return (TEnum) Enum.Parse(typeof(TEnum), value);
    }

}

public class EnumUtils : EnumClassUtils<Enum>
{
}

Usage:

EnumUtils.Parse<SomeEnum>("value");

Note: this is specifically stated in the C# 5.0 language specification:

If type parameter S depends on type parameter T then: [...] It is valid for S to have the value type constraint and T to have the reference type constraint. Effectively this limits T to the types System.Object, System.ValueType, System.Enum, and any interface type.

@David I. McIntosh 2015-04-11 19:20:23

Also, can you elaborate on your answer a bit. The comment you include from the language spec tells us your Enum type has to be System.Object, System.FalueType, System.Enum or any interface. How is it further restricted to just the System.Enum type? Wouldn't you need to do public class EnumUtils : EnumClassUtils<Enum> where Enum : struct, IConvertible? thanks.

@Julien Lebosquain 2015-04-11 20:51:49

@DavidI.McIntosh EnumClassUtils<System.Enum> is sufficient to restrict T to any System.Enum and any derived types. struct on Parse then restricts it further to a real enum type. You need to restrict to Enum at some point. To do so, your class has to be nested. See gist.github.com/MrJul/7da12f5f2d6c69f03d79

@David I. McIntosh 2015-04-12 01:00:23

Ah, I see how it works now. Then use of the class must always be via reference to it as a nested class - not pleasant, but I guess that's the best that can be hoped for. Thanks for the prompt reply by the way.

@David I. McIntosh 2015-04-12 15:08:17

Just to be clear, my comment "not pleasant" was not a comment on your solution - it is really a beautiful hack. Just "not pleasant" that MS forces us to use such a convoluted hack.

@Mord Zuber 2015-04-29 15:11:38

Is there a way to work this to also be useable for extension methods?

@Julien Lebosquain 2015-05-06 13:07:50

@Max Unfortunately, I don't think so :(

@John Gietzen 2015-05-09 22:01:03

I would add an internal constructor, to prevent this class from being inherited in strange ways.

@bubbleking 2016-08-07 20:03:41

Wouldn't it be nice if you could do this: where T : class, struct

@tsemer 2017-01-04 16:50:01

What does the where TClass : class constraint gain here?

@Marc L. 2017-07-14 12:30:20

Is there a way to further constrain TEnum so that int v; TEnum e = (TEnum) v; is allowed?

@Triynko 2018-05-15 21:56:14

It's about time! And it BETTER support int v; TEnum e = (TEnum)v;, that is to say it better be smart enough to realize all Enums are ints! Don't **** it up Microsoft!

@M.Stramm 2018-12-03 12:00:13

@Trinkyo enum DefinitelyNotAnInt : byte { Realize, That, I, Am, Not, An, Int } enum AlsoNotAnInt : long { Well, Bummer }

@That Marc 2019-05-26 15:56:10

Despite the fact this works in c# 7.3 and upwards, it is somewhat silly that having nullable property of a type T with constraint where T: Enum the compiles still cries like a baby that T must be of a non-nullable type, resulting that in whole hierarchy of generics being passed one must specify struct constraint as well. Damn...

@Christopher Currens 2011-11-10 21:46:36

This feature is finally supported in C# 7.3!

The following snippet (from the dotnet samples) demonstrates how:

public static Dictionary<int, string> EnumNamedValues<T>() where T : System.Enum
{
    var result = new Dictionary<int, string>();
    var values = Enum.GetValues(typeof(T));

    foreach (int item in values)
        result.Add(item, Enum.GetName(typeof(T), item));
    return result;
}

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

// license: http://www.apache.org/licenses/LICENSE-2.0.html
.assembly MyThing{}
.class public abstract sealed MyThing.Thing
       extends [mscorlib]System.Object
{
  .method public static !!T  GetEnumFromString<valuetype .ctor ([mscorlib]System.Enum) T>(string strValue,
                                                                                          !!T defaultValue) cil managed
  {
    .maxstack  2
    .locals init ([0] !!T temp,
                  [1] !!T return_value,
                  [2] class [mscorlib]System.Collections.IEnumerator enumerator,
                  [3] class [mscorlib]System.IDisposable disposer)
    // if(string.IsNullOrEmpty(strValue)) return defaultValue;
    ldarg strValue
    call bool [mscorlib]System.String::IsNullOrEmpty(string)
    brfalse.s HASVALUE
    br RETURNDEF         // return default it empty
    
    // foreach (T item in Enum.GetValues(typeof(T)))
  HASVALUE:
    // Enum.GetValues.GetEnumerator()
    ldtoken !!T
    call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
    call class [mscorlib]System.Array [mscorlib]System.Enum::GetValues(class [mscorlib]System.Type)
    callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Array::GetEnumerator() 
    stloc enumerator
    .try
    {
      CONDITION:
        ldloc enumerator
        callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
        brfalse.s LEAVE
        
      STATEMENTS:
        // T item = (T)Enumerator.Current
        ldloc enumerator
        callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()
        unbox.any !!T
        stloc temp
        ldloca.s temp
        constrained. !!T
        
        // if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
        callvirt instance string [mscorlib]System.Object::ToString()
        callvirt instance string [mscorlib]System.String::ToLower()
        ldarg strValue
        callvirt instance string [mscorlib]System.String::Trim()
        callvirt instance string [mscorlib]System.String::ToLower()
        callvirt instance bool [mscorlib]System.String::Equals(string)
        brfalse.s CONDITION
        ldloc temp
        stloc return_value
        leave.s RETURNVAL
        
      LEAVE:
        leave.s RETURNDEF
    }
    finally
    {
        // ArrayList's Enumerator may or may not inherit from IDisposable
        ldloc enumerator
        isinst [mscorlib]System.IDisposable
        stloc.s disposer
        ldloc.s disposer
        ldnull
        ceq
        brtrue.s LEAVEFINALLY
        ldloc.s disposer
        callvirt instance void [mscorlib]System.IDisposable::Dispose()
      LEAVEFINALLY:
        endfinally
    }
  
  RETURNDEF:
    ldarg defaultValue
    stloc return_value
  
  RETURNVAL:
    ldloc return_value
    ret
  }
} 

Which generates a function that would look like this, if it were valid C#:

T GetEnumFromString<T>(string valueString, T defaultValue) where T : Enum

Then with the following C# code:

using MyThing;
// stuff...
private enum MyEnum { Yes, No, Okay }
static void Main(string[] args)
{
    Thing.GetEnumFromString("No", MyEnum.Yes); // returns MyEnum.No
    Thing.GetEnumFromString("Invalid", MyEnum.Okay);  // returns MyEnum.Okay
    Thing.GetEnumFromString("AnotherInvalid", 0); // compiler error, not an Enum
}

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:

ilasm.exe /DLL /OUTPUT=MyThing.netmodule

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#.

type MyThing =
    static member GetEnumFromString<'T when 'T :> Enum> str defaultValue: 'T =
        /// protect for null (only required in interop with C#)
        let str = if isNull str then String.Empty else str

        Enum.GetValues(typedefof<'T>)
        |> Seq.cast<_>
        |> Seq.tryFind(fun v -> String.Compare(v.ToString(), str.Trim(), true) = 0)
        |> function Some x -> x | None -> defaultValue

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:

// works, result is inferred to have type StringComparison
var result = MyThing.GetEnumFromString("OrdinalIgnoreCase", StringComparison.Ordinal);
// type restriction is recognized by C#, this fails at compile time
var result = MyThing.GetEnumFromString("OrdinalIgnoreCase", 42);

@TonyG 2012-03-25 01:31:14

Yeah, very hardcore. I have the utmost respect for someone who can code in IL, and know how the features are supported at the higher language level - a level which many of us still see as being low level under applications, business rules, UI's, component libraries, etc.

@Christopher Currens 2012-04-23 07:09:07

@ruslan - it says that you can't accomplish this in c# in the first paragraph of the answer. Thats actually what this answer is showing: very possible in cil (since the code above works successfully when used in other .net languages), however not possible in C# by itself.

@MgSam 2012-09-19 00:30:39

What I'd really like to know is why the C# team hasn't started allowing this yet, since it is already supported by MSIL.

@Christopher Currens 2012-09-19 00:58:36

@MgSam - From Eric Lippert: There's no particularly unusual reason why not; we have lots of other things to do, limited budgets, and this one has never made it past the "wouldn't this be nice?" discussion in the language design team.

@Lord of Scripts 2013-01-14 16:30:04

Having worked with a mix of C and Assembler (various) for many years out of necessity, I can only say I stay away from those unmaintainable things. One thing I have never understood though, is WHY does the .NET team doesn't support this if MSIL has the capability of doing it and there are so many people asking for it and having to write some horrible workarounds.

@supercat 2013-04-12 16:27:31

@LordofScripts: I think the reason is that that since a class which constrains a T to System.Enum wouldn't be able to do all the things with T that people might expect, the authors of C# figured they may as well forbid it altogether. I consider the decision unfortunate, since it C# had simply ignored any special handling of System.Enum constraints, it would have been possible to write a HasAnyFlags<T>(this T it, T other) extension method that was orders of magnitude faster than Enum.HasFlag(Enum) and which type-checked its arguments.

@Michael Blackburn 2015-10-26 19:40:06

I don't think I've ever had a project where I didn't end up here. C#6 is 110% syntactic sugar and THIS didn't get in? Cut the crap.

@Christopher Currens 2015-10-26 21:22:48

@MichaelBlackburn It's more complicated than it sounds, mostly due to bit flags on enums. A github user named HaloFour gives a good summary in this Roslyn issue.

@quetzalcoatl 2017-08-01 00:07:53

Just my three cents.. Since System.Enum is too special to get sexy handling, and since we've already got where T:class, where T:struct, etc, then where T:enum seems a pretty badass and probably trivial to implement..

@jpmc26 2018-12-20 23:58:44

What versions of .NET implement C# 7.3?

@That Marc 2019-05-26 15:54:47

Despite the fact this works in c# 7.3 and upwards, it is somewhat silly that having nullable property of a type T with constraint where T: Enum the compiles still cries like a baby that T must be of a non-nullable type, resulting that in whole hierarchy of generics being passed one must specify struct constraint as well. Damn...

@Jeppe Stig Nielsen 2020-05-22 07:39:45

Yes, it is better to say where T : struct, System.Enum because then the compiler will know it is always a value type. As @ThatMarc just said, it is needed if you want to use T? (Nullable<T>). It could also help you in other situations, like T t = …; if (t == null) { … }, where the compiler will give a helpful message if it knows T is a value type.

@Rodney P. Barbati 2018-05-30 04:11:57

Just for completeness, the following is a Java solution. I am certain the same could be done in C# as well. It avoids having to specify the type anywhere in code - instead, you specify it in the strings you are trying to parse.

The problem is that there isn't any way to know which enumeration the String might match - so the answer is to solve that problem.

Instead of accepting just the string value, accept a String that has both the enumeration and the value in the form "enumeration.value". Working code is below - requires Java 1.8 or later. This would also make the XML more precise as in you would see something like color="Color.red" instead of just color="red".

You would call the acceptEnumeratedValue() method with a string containing the enum name dot value name.

The method returns the formal enumerated value.

import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;


public class EnumFromString {

    enum NumberEnum {One, Two, Three};
    enum LetterEnum {A, B, C};


    Map<String, Function<String, ? extends Enum>> enumsByName = new HashMap<>();

    public static void main(String[] args) {
        EnumFromString efs = new EnumFromString();

        System.out.print("\nFirst string is NumberEnum.Two - enum is " + efs.acceptEnumeratedValue("NumberEnum.Two").name());
        System.out.print("\nSecond string is LetterEnum.B - enum is " + efs.acceptEnumeratedValue("LetterEnum.B").name());

    }

    public EnumFromString() {
        enumsByName.put("NumberEnum", s -> {return NumberEnum.valueOf(s);});
        enumsByName.put("LetterEnum", s -> {return LetterEnum.valueOf(s);});
    }

    public Enum acceptEnumeratedValue(String enumDotValue) {

        int pos = enumDotValue.indexOf(".");

        String enumName = enumDotValue.substring(0, pos);
        String value = enumDotValue.substring(pos + 1);

        Enum enumeratedValue = enumsByName.get(enumName).apply(value);

        return enumeratedValue;
    }


}

@baumgarb 2018-05-10 09:55:13

It should also be considered that since the release of C# 7.3 using Enum constraints is supported out-of-the-box without having to do additional checking and stuff.

So going forward and given you've changed the language version of your project to C# 7.3 the following code is going to work perfectly fine:

    private static T GetEnumFromString<T>(string value, T defaultValue) where T : Enum
    {
        // Your code goes here...
    }

In case you're don't know how to change the language version to C# 7.3 see the following screenshot: enter image description here

EDIT 1 - Required Visual Studio Version and considering ReSharper

For Visual Studio to recognize the new syntax you need at least version 15.7. You can find that also mentioned in Microsoft's release notes, see Visual Studio 2017 15.7 Release Notes. Thanks @MohamedElshawaf for pointing out this valid question.

Pls also note that in my case ReSharper 2018.1 as of writing this EDIT does not yet support C# 7.3. Having ReSharper activated it highlights the Enum constraint as an error telling me Cannot use 'System.Array', 'System.Delegate', 'System.Enum', 'System.ValueType', 'object' as type parameter constraint. ReSharper suggests as a quick fix to Remove 'Enum' constraint of type paramter T of method

However, if you turn off ReSharper temporarily under Tools -> Options -> ReSharper Ultimate -> General you'll see that the syntax is perfectly fine given that you use VS 15.7 or higher and C# 7.3 or higher.

@mshwf 2018-05-11 11:10:57

What VS version are you using?

@Patrick Roberts 2018-05-11 15:37:28

@MohamedElshawaf I believe it's version 15.7 that contains support for C# 7.3

@Mariusz Pawelski 2018-05-11 18:37:46

I think it's better to write where T : struct, Enum, to avoid passing System.Enum itself as type parameter.

@Stephen Kennedy 2018-06-18 18:52:43

Like @MariuszPawelski I write struct, Enum. My rationale is explained in the answer and comments here.

@DalSoft 2018-11-06 16:22:16

The ReSharper info really helped me. Note latest preview version supports this feature.

@Sam I am says Reinstate Monica 2019-04-30 22:26:14

I'm surprised it took this long

@uluorta 2017-11-23 14:17:57

If it's ok to use direct casting afterwards, I guess you can use the System.Enum base class in your method, wherever necessary. You just need to replace the type parameters carefully. So the method implementation would be like:

public static class EnumUtils
{
    public static Enum GetEnumFromString(string value, Enum defaultValue)
    {
        if (string.IsNullOrEmpty(value)) return defaultValue;
        foreach (Enum item in Enum.GetValues(defaultValue.GetType()))
        {
            if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
        }
        return defaultValue;
    }
}

Then you can use it like:

var parsedOutput = (YourEnum)EnumUtils.GetEnumFromString(someString, YourEnum.DefaultValue);

@DiskJunky 2018-03-27 21:17:13

use of Enum.ToObject() would yield a more flexible result. Added to which, you could do the string comparisons without case sensitivity which would negate the need to call ToLower()

@BatteryBackupUnit 2017-07-24 06:49:10

As stated in other answers before; while this cannot be expressed in source-code it can actually be done on IL Level. @Christopher Currens answer shows how the IL do to that.

With Fodys Add-In ExtraConstraints.Fody there's a very simple way, complete with build-tooling, to achieve this. Just add their nuget packages (Fody, ExtraConstraints.Fody) to your project and add the constraints as follows (Excerpt from the Readme of ExtraConstraints):

public void MethodWithEnumConstraint<[EnumConstraint] T>() {...}

public void MethodWithTypeEnumConstraint<[EnumConstraint(typeof(ConsoleColor))] T>() {...}

and Fody will add the necessary IL for the constraint to be present. Also note the additional feature of constraining delegates:

public void MethodWithDelegateConstraint<[DelegateConstraint] T> ()
{...}

public void MethodWithTypeDelegateConstraint<[DelegateConstraint(typeof(Func<int>))] T> ()
{...}

Regarding Enums, you might also want to take note of the highly interesting Enums.NET.

@Basheer AL-MOMANI 2016-11-01 09:29:45

I created an extension Method to get integer value from enum take look at method implementation

public static int ToInt<T>(this T soure) where T : IConvertible//enum
{
    if (typeof(T).IsEnum)
    {
        return (int) (IConvertible)soure;// the tricky part
    }
    //else
    //    throw new ArgumentException("T must be an enumerated type");
    return soure.ToInt32(CultureInfo.CurrentCulture);
}

this is usage

MemberStatusEnum.Activated.ToInt()// using extension Method
(int) MemberStatusEnum.Activated //the ordinary way

@quetzalcoatl 2017-08-01 00:13:47

While it probably works, it has almost no relevance to the question.

@Yahoo Serious 2013-05-24 14:07:25

Edit

The question has now superbly been answered by Julien Lebosquain. I would also like to extend his answer with ignoreCase, defaultValue and optional arguments, while adding TryParse and ParseOrDefault.

public abstract class ConstrainedEnumParser<TClass> where TClass : class
// value type constraint S ("TEnum") depends on reference type T ("TClass") [and on struct]
{
    // internal constructor, to prevent this class from being inherited outside this code
    internal ConstrainedEnumParser() {}
    // Parse using pragmatic/adhoc hard cast:
    //  - struct + class = enum
    //  - 'guaranteed' call from derived <System.Enum>-constrained type EnumUtils
    public static TEnum Parse<TEnum>(string value, bool ignoreCase = false) where TEnum : struct, TClass
    {
        return (TEnum)Enum.Parse(typeof(TEnum), value, ignoreCase);
    }
    public static bool TryParse<TEnum>(string value, out TEnum result, bool ignoreCase = false, TEnum defaultValue = default(TEnum)) where TEnum : struct, TClass // value type constraint S depending on T
    {
        var didParse = Enum.TryParse(value, ignoreCase, out result);
        if (didParse == false)
        {
            result = defaultValue;
        }
        return didParse;
    }
    public static TEnum ParseOrDefault<TEnum>(string value, bool ignoreCase = false, TEnum defaultValue = default(TEnum)) where TEnum : struct, TClass // value type constraint S depending on T
    {
        if (string.IsNullOrEmpty(value)) { return defaultValue; }
        TEnum result;
        if (Enum.TryParse(value, ignoreCase, out result)) { return result; }
        return defaultValue;
    }
}

public class EnumUtils: ConstrainedEnumParser<System.Enum>
// reference type constraint to any <System.Enum>
{
    // call to parse will then contain constraint to specific <System.Enum>-class
}

Examples of usage:

WeekDay parsedDayOrArgumentException = EnumUtils.Parse<WeekDay>("monday", ignoreCase:true);
WeekDay parsedDayOrDefault;
bool didParse = EnumUtils.TryParse<WeekDay>("clubs", out parsedDayOrDefault, ignoreCase:true);
parsedDayOrDefault = EnumUtils.ParseOrDefault<WeekDay>("friday", ignoreCase:true, defaultValue:WeekDay.Sunday);

Old

My old improvements on Vivek's answer by using the comments and 'new' developments:

  • use TEnum for clarity for users
  • add more interface-constraints for additional constraint-checking
  • let TryParse handle ignoreCase with the existing parameter (introduced in VS2010/.Net 4)
  • optionally use the generic default value (introduced in VS2005/.Net 2)
  • use optional arguments(introduced in VS2010/.Net 4) with default values, for defaultValue and ignoreCase

resulting in:

public static class EnumUtils
{
    public static TEnum ParseEnum<TEnum>(this string value,
                                         bool ignoreCase = true,
                                         TEnum defaultValue = default(TEnum))
        where TEnum : struct,  IComparable, IFormattable, IConvertible
    {
        if ( ! typeof(TEnum).IsEnum) { throw new ArgumentException("TEnum must be an enumerated type"); }
        if (string.IsNullOrEmpty(value)) { return defaultValue; }
        TEnum lResult;
        if (Enum.TryParse(value, ignoreCase, out lResult)) { return lResult; }
        return defaultValue;
    }
}

@Vivek 2008-09-17 04:13:40

Since Enum Type implements IConvertible interface, a better implementation should be something like this:

public T GetEnumFromString<T>(string value) where T : struct, IConvertible
{
   if (!typeof(T).IsEnum) 
   {
      throw new ArgumentException("T must be an enumerated type");
   }

   //...
}

This will still permit passing of value types implementing IConvertible. The chances are rare though.

@Maslow 2009-05-29 18:28:17

this appears to only be vs2008 and newer, right? or maybe it's just not in vb2005?

@Vivek 2009-06-01 17:07:16

Generics are available since .NET 2.0. Hence they are available in vb 2005 as well.

@Ricardo Nolde 2010-09-27 18:42:39

Well, make it even more constrained then, if you choose to go down this path... use "class TestClass<T> where T : struct, IComparable, IFormattable, IConvertible"

@Lisa 2011-11-24 06:52:39

Another suggestion is to define the generic type with the identifier TEnum. Thus: public TEnum GetEnumFromString<TEnum>(string value) where TEnum : struct, IConvertible, IComparible, IFormattable { }

@russbishop 2014-03-05 18:04:06

You don't gain much by including the other interfaces because almost all of the built-in value types implement all of those interfaces. This is especially true for constraints on a generic extension method, which is extremely handy for operating on enums, except for the fact that those extension methods are like a virus that infects all your objects. IConvertable at least narrows it down quite a bit.

@John Alexiou 2014-11-07 14:59:51

Of course this accepted answer notes the best exception for genetic types is NotSupportedExection() and not ArgumentException().

@Sam I am says Reinstate Monica 2015-02-12 22:05:15

doesn't quite get the compile-time checking that I'm looking for

@David I. McIntosh 2015-04-12 01:55:22

@SamIam : When you posted, this thread was what, 6 and a half years old, and you were correct, no compile-time checking in any of the answers. Then only 3 days later, after 6 years, you got your wish - see Julien Lebosquain's post way below.

@Jelle Fresen 2015-12-16 08:49:05

Indeed, David, as time has passed by and both the language and the combined community knowledge has evolved, we now have a better solution thanks to Julien Lebosquain. Downvoted this answer not because it is bad, but because Julien's answer is better and should surpass this one in votes. Please notify me if that's frowned upon.

@Scott 2017-03-14 15:44:27

@Lisa - there is a spelling error in your example which causes it not to work, I believe it should be IComparable not IComparible (a not i)

@tchelidze 2017-04-06 06:24:10

for .net core use typeof(T).GetTypeInfo().IsEnum

@Lisa 2017-04-10 06:10:00

@Scott You're quite right. But I can't edit my comment it seems. Sorry.

@baumgarb 2018-05-10 09:56:52

Very old topic, but there's been a huge improvement since C# 7.3. It's now fully supported to use Enum constraints. See my longer answer all the way down at the bottom.

@AustinWBryan 2018-05-16 01:23:40

Thing is, yes I've limited it down to almost only enums, but the C# compiler is still saying I can't do operations like x | y; with them, because there's still a chance they're not enums.

@Yahoo Serious 2018-06-15 09:44:49

This feature is supported as of C# 7.3

@KarmaEDV 2014-03-25 13:40:43

This is my take at it. Combined from the answers and MSDN

public static TEnum ParseToEnum<TEnum>(this string text) where TEnum : struct, IConvertible, IComparable, IFormattable
{
    if (string.IsNullOrEmpty(text) || !typeof(TEnum).IsEnum)
        throw new ArgumentException("TEnum must be an Enum type");

    try
    {
        var enumValue = (TEnum)Enum.Parse(typeof(TEnum), text.Trim(), true);
        return enumValue;
    }
    catch (Exception)
    {
        throw new ArgumentException(string.Format("{0} is not a member of the {1} enumeration.", text, typeof(TEnum).Name));
    }
}

MSDN Source

@Nick 2014-08-28 19:17:18

This doesn't really make sense. If TEnum actually is an Enum type but text is an empty string then you get an ArgumentException saying "TEnum must be an Enum type" even though it is.

@niaher 2013-07-25 07:55:49

I've encapsulated Vivek's solution into a utility class that you can reuse. Please note that you still should define type constraints "where T : struct, IConvertible" on your type.

using System;

internal static class EnumEnforcer
{
    /// <summary>
    /// Makes sure that generic input parameter is of an enumerated type.
    /// </summary>
    /// <typeparam name="T">Type that should be checked.</typeparam>
    /// <param name="typeParameterName">Name of the type parameter.</param>
    /// <param name="methodName">Name of the method which accepted the parameter.</param>
    public static void EnforceIsEnum<T>(string typeParameterName, string methodName)
        where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum)
        {
            string message = string.Format(
                "Generic parameter {0} in {1} method forces an enumerated type. Make sure your type parameter {0} is an enum.",
                typeParameterName,
                methodName);

            throw new ArgumentException(message);
        }
    }

    /// <summary>
    /// Makes sure that generic input parameter is of an enumerated type.
    /// </summary>
    /// <typeparam name="T">Type that should be checked.</typeparam>
    /// <param name="typeParameterName">Name of the type parameter.</param>
    /// <param name="methodName">Name of the method which accepted the parameter.</param>
    /// <param name="inputParameterName">Name of the input parameter of this page.</param>
    public static void EnforceIsEnum<T>(string typeParameterName, string methodName, string inputParameterName)
        where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum)
        {
            string message = string.Format(
                "Generic parameter {0} in {1} method forces an enumerated type. Make sure your input parameter {2} is of correct type.",
                typeParameterName,
                methodName,
                inputParameterName);

            throw new ArgumentException(message);
        }
    }

    /// <summary>
    /// Makes sure that generic input parameter is of an enumerated type.
    /// </summary>
    /// <typeparam name="T">Type that should be checked.</typeparam>
    /// <param name="exceptionMessage">Message to show in case T is not an enum.</param>
    public static void EnforceIsEnum<T>(string exceptionMessage)
        where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum)
        {
            throw new ArgumentException(exceptionMessage);
        }
    }
}

@Martin 2010-12-16 11:24:29

I tried to improve the code a bit:

public T LoadEnum<T>(string value, T defaultValue = default(T)) where T : struct, IComparable, IFormattable, IConvertible
{
    if (Enum.IsDefined(typeof(T), value))
    {
        return (T)Enum.Parse(typeof(T), value, true);
    }
    return defaultValue;
}

@styfle 2017-11-02 15:55:59

This is better than the accepted answer because it allows you to call defaultValue.ToString("D", System.Globalization.NumberFormatInfo.CurrentInfo) even though you don't know which type of enum it is, only that the object is an enum.

@Nyerguds 2018-03-06 15:03:53

The advance check with IsDefined will ruin the case insensitivity, though. Unlike Parse, IsDefined has no ignoreCase argument, and MSDN says it only matches exact case.

@dimarzionist 2008-09-17 02:00:07

Hope this is helpful:

public static TValue ParseEnum<TValue>(string value, TValue defaultValue)
                  where TValue : struct // enum 
{
      try
      {
            if (String.IsNullOrEmpty(value))
                  return defaultValue;
            return (TValue)Enum.Parse(typeof (TValue), value);
      }
      catch(Exception ex)
      {
            return defaultValue;
      }
}

@Paulo Santos 2010-01-28 10:04:29

If you need case insensitivity, simply replace return (TValue)Enum.Parse(typeof (TValue), value); by return (TValue)Enum.Parse(typeof (TValue), value, true);

@Bivoauc 2008-09-17 02:24:14

I modified the sample by dimarzionist. This version will only work with Enums and not let structs get through.

public static T ParseEnum<T>(string enumString)
    where T : struct // enum 
    {
    if (String.IsNullOrEmpty(enumString) || !typeof(T).IsEnum)
       throw new Exception("Type given must be an Enum");
    try
    {

       return (T)Enum.Parse(typeof(T), enumString, true);
    }
    catch (Exception ex)
    {
       return default(T);
    }
}

@Mark Simpson 2010-06-25 21:59:26

I wouldn't return the default value on failure; I'd let the exception propagate (just as it does with Enum.Parse). Instead, use TryParse returning a bool and return the result using an out param.

@Konrad Morawski 2012-04-04 06:46:30

OP wants it to be case-insensitive, this is not.

@expert 2012-04-23 01:23:29

I loved Christopher Currens's solution using IL but for those who don't want to deal with tricky business of including MSIL into their build process I wrote similar function in C#.

Please note though that you can't use generic restriction like where T : Enum because Enum is special type. Therefore I have to check if given generic type is really enum.

My function is:

public static T GetEnumFromString<T>(string strValue, T defaultValue)
{
    // Check if it realy enum at runtime 
    if (!typeof(T).IsEnum)
        throw new ArgumentException("Method GetEnumFromString can be used with enums only");

    if (!string.IsNullOrEmpty(strValue))
    {
        IEnumerator enumerator = Enum.GetValues(typeof(T)).GetEnumerator();
        while (enumerator.MoveNext())
        {
            T temp = (T)enumerator.Current;
            if (temp.ToString().ToLower().Equals(strValue.Trim().ToLower()))
                return temp;
        }
    }

    return defaultValue;
}

@Jeff 2010-10-04 23:09:58

I always liked this (you could modify as appropriate):

public static IEnumerable<TEnum> GetEnumValues()
{
  Type enumType = typeof(TEnum);

  if(!enumType.IsEnum)
    throw new ArgumentException("Type argument must be Enum type");

  Array enumValues = Enum.GetValues(enumType);
  return enumValues.Cast<TEnum>();
}

@Sunny Rajwadi 2010-04-18 05:38:20

I do have specific requirement where I required to use enum with text associated with enum value. For example when I use enum to specify error type it required to describe error details.

public static class XmlEnumExtension
{
    public static string ReadXmlEnumAttribute(this Enum value)
    {
        if (value == null) throw new ArgumentNullException("value");
        var attribs = (XmlEnumAttribute[]) value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof (XmlEnumAttribute), true);
        return attribs.Length > 0 ? attribs[0].Name : value.ToString();
    }

    public static T ParseXmlEnumAttribute<T>(this string str)
    {
        foreach (T item in Enum.GetValues(typeof(T)))
        {
            var attribs = (XmlEnumAttribute[])item.GetType().GetField(item.ToString()).GetCustomAttributes(typeof(XmlEnumAttribute), true);
            if(attribs.Length > 0 && attribs[0].Name.Equals(str)) return item;
        }
        return (T)Enum.Parse(typeof(T), str, true);
    }
}

public enum MyEnum
{
    [XmlEnum("First Value")]
    One,
    [XmlEnum("Second Value")]
    Two,
    Three
}

 static void Main()
 {
    // Parsing from XmlEnum attribute
    var str = "Second Value";
    var me = str.ParseXmlEnumAttribute<MyEnum>();
    System.Console.WriteLine(me.ReadXmlEnumAttribute());
    // Parsing without XmlEnum
    str = "Three";
    me = str.ParseXmlEnumAttribute<MyEnum>();
    System.Console.WriteLine(me.ReadXmlEnumAttribute());
    me = MyEnum.One;
    System.Console.WriteLine(me.ReadXmlEnumAttribute());
}

@Andrew Backer 2009-07-07 17:00:47

Interestingly enough, apparently this is possible in other langauges (Managed C++, IL directly).

To Quote:

... Both constraints actually produce valid IL and can also be consumed by C# if written in another language (you can declare those constraints in managed C++ or in IL).

Who knows

@Ben Voigt 2011-03-27 03:30:18

Managed Extensions for C++ don't have ANY support for generics, I think you mean C++/CLI.

@Karg 2008-09-17 02:32:54

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#.

internal sealed class GenericTypeThatRequiresAnEnum<T> {
    static GenericTypeThatRequiresAnEnum() {
        if (!typeof(T).IsEnum) {
        throw new ArgumentException("T must be an enumerated type");
        }
    }
}

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.

@McGarnagle 2015-02-09 19:18:50

This is a good option for generic classes -- but of course, it doesn't help for generic methods.

@jrh 2018-10-09 12:42:21

Also, this is also not enforced at compile time, you would only know you provided a non Enum T when the constructor executed. Though this is much nicer than waiting for an instance constructor.

Related Questions

Sponsored Content

30 Answered Questions

[SOLVED] How can I cast int to enum?

  • 2008-08-27 03:58:21
  • lomaxx
  • 1422708 View
  • 3246 Score
  • 30 Answer
  • Tags:   c# enums casting int

12 Answered Questions

[SOLVED] What does the [Flags] Enum Attribute mean in C#?

  • 2008-08-12 04:09:16
  • Brian Leahy
  • 485632 View
  • 1469 Score
  • 12 Answer
  • Tags:   c# enums flags

27 Answered Questions

[SOLVED] How to get an enum value from a string value in Java?

  • 2009-03-02 22:56:34
  • Malachi
  • 1150100 View
  • 2008 Score
  • 27 Answer
  • Tags:   java enums

14 Answered Questions

[SOLVED] Comparing Java enum members: == or equals()?

  • 2009-11-17 17:26:27
  • Matt Ball
  • 683016 View
  • 1760 Score
  • 14 Answer
  • Tags:   java enums

27 Answered Questions

[SOLVED] Get int value from enum in C#

  • 2009-06-03 06:46:39
  • jim
  • 1582426 View
  • 1865 Score
  • 27 Answer
  • Tags:   c# enums casting int

29 Answered Questions

[SOLVED] How to enumerate an enum

27 Answered Questions

[SOLVED] JavaScriptSerializer - JSON serialization of enum as string

37 Answered Questions

[SOLVED] String representation of an Enum

  • 2009-01-08 14:15:55
  • user29964
  • 766575 View
  • 917 Score
  • 37 Answer
  • Tags:   c# enums

Sponsored Content