1414

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) Christopher Currens has 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.

EDIT 2: (13th Apr 2021) As this has now been addressed, and supported, since C# 7.3, I have changed the accepted answer, though full perusal of the top answers is worth it for academic, and historical, interest :)

johnc
  • 39,385
  • 37
  • 101
  • 139
  • Why are extension methods only for reference types? – Shimmy Weitzhandler Aug 18 '09 at 12:08
  • 34
    @Shimmy: As soon as you pass a value type to the extension method, you're working on a copy of it, so you can't change its state. – Garo Yeriazarian Jun 25 '10 at 21:52
  • 5
    Know it is an old thread, don't know if they changed things, but extension methods works fine for value types, sure they might not always make as much sense, but I have used "public static TimeSpan Seconds(this int x) { return TimeSpan.FromSeconds(x); }" to enable the syntax of "Wait.For(5.Seconds())..." – Jens Mar 05 '12 at 20:01
  • 12
    Maybe you [should use](http://stackoverflow.com/questions/9033/hidden-features-of-c#12137) ToUpperInvariant() instead of ToLower()... – Massimiliano Sep 19 '08 at 13:07
  • 7
    Realize this wasn't part of the question, but you could improve your foreach loop logic by using String.Equals with StringComparison.InvariantCultureIgnoreCase – Firestrand Nov 02 '12 at 15:01
  • 2
    possible duplicate of [Anyone know a good workaround for the lack of an enum generic constraint?](http://stackoverflow.com/questions/7244/anyone-know-a-good-workaround-for-the-lack-of-an-enum-generic-constraint) – nawfal Mar 02 '13 at 21:02
  • 3
    Why use that `foreach`-loop? `Enum.Parse` contains an `ignoreCase`-parameter (I guess since [.Net 2.0](http://msdn.microsoft.com/en-us/library/kxydatf9%28v=vs.80%29.aspx)). – Yahoo Serious May 23 '13 at 15:08
  • I went ahead and added an [answer](http://stackoverflow.com/a/16736914/422877) with the existing `ignoreCase`-parameter and a generic `default` value as optional arguments, and some more improvements suggested by others. – Yahoo Serious May 24 '13 at 14:10
  • 1
    Worth noting that Enum.Parse can handle an enum with the [Flags] attribute, provided values in the string are separated with commas. – yoyo Nov 22 '13 at 19:49
  • Please post your solution as an answer post. Don't include answers in questions. – BartoszKP Sep 28 '15 at 21:07
  • How is the value string created? If this was created using the `Enum.ToString()` method, and the Enum type is marked with the `[Flags]` attribute, the `defaultValue` will always be returned from the method with `value = (enum.type1 | enum.type2).ToString() == type1, type2`. Corner case. – paxmemento Oct 16 '15 at 19:24
  • 1
    check this answer written by me http://stackoverflow.com/a/38410351/4009642 – Ahmed Fwela Jul 18 '16 at 01:23
  • @bigworld12 The initial requirement for me to do this is long lost in the mists of time, but very comprehensive solution nonetheless :) – johnc Jul 25 '16 at 02:20
  • 1
    Implementing this functionality is on the drawing board for C# 7! Vote it up! https://github.com/dotnet/roslyn/issues/262 – Airn5475 Sep 13 '16 at 13:09
  • 3
    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. – baumgarb May 10 '18 at 09:56
  • 1
    NB: this feature is supported as of C# 7.3 – Yahoo Serious Jun 15 '18 at 09:44
  • 1
    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. – That Marc May 26 '19 at 15:56
  • Starting from .NET Core 2.0 (from 2017), the class library has generic overloads that allow you to write stuff like `var val = Enum.Parse("ThuRSday", ignoreCase: true);`. – Jeppe Stig Nielsen Jun 15 '22 at 14:21

22 Answers22

1098

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.

shA.t
  • 16,580
  • 5
  • 54
  • 111
Vivek
  • 16,360
  • 5
  • 30
  • 37
  • this appears to only be vs2008 and newer, right? or maybe it's just not in vb2005? – Maslow May 29 '09 at 18:28
  • 2
    Generics are available since .NET 2.0. Hence they are available in vb 2005 as well. – Vivek Jun 01 '09 at 17:07
  • 51
    Well, make it even more constrained then, if you choose to go down this path... use "class TestClass where T : struct, IComparable, IFormattable, IConvertible" – Ricardo Nolde Sep 27 '10 at 18:42
  • 113
    Another suggestion is to define the generic type with the identifier TEnum. Thus: public TEnum GetEnumFromString(string value) where TEnum : struct, IConvertible, IComparible, IFormattable { } – Lisa Nov 24 '11 at 06:52
  • 18
    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. – russbishop Mar 05 '14 at 18:04
  • 2
    Of course [this](http://stackoverflow.com/a/1412761) accepted answer notes the best exception for genetic types is `NotSupportedExection()` and not `ArgumentException()`. – John Alexiou Nov 07 '14 at 14:59
  • doesn't quite get the compile-time checking that I'm looking for – Sam I am says Reinstate Monica Feb 12 '15 at 22:05
  • 2
    @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. – David I. McIntosh Apr 12 '15 at 01:55
  • 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. – Jelle Fresen Dec 16 '15 at 08:49
  • 1
    @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) – Scott Mar 14 '17 at 15:44
  • 1
    for .net core use **typeof(T).GetTypeInfo().IsEnum** – tchelidze Apr 06 '17 at 06:24
  • @Scott You're quite right. But I can't edit my comment it seems. Sorry. – Lisa Apr 10 '17 at 06:10
  • 8
    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. – baumgarb May 10 '18 at 09:56
  • 8
    This feature is supported as of C# 7.3 – Yahoo Serious Jun 15 '18 at 09:44
902

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);
Community
  • 1
  • 1
Christopher Currens
  • 29,917
  • 5
  • 57
  • 77
  • 79
    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. – TonyG Mar 25 '12 at 01:31
  • 2
    @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. – Christopher Currens Apr 23 '12 at 07:09
  • 13
    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. – MgSam Sep 19 '12 at 00:30
  • 30
    @MgSam - From [Eric Lippert](http://stackoverflow.com/a/1331811/721276): `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.` – Christopher Currens Sep 19 '12 at 00:58
  • 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. – Lord of Scripts Jan 14 '13 at 16:30
  • 6
    @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(this T it, T other)` extension method that was orders of magnitude faster than `Enum.HasFlag(Enum)` and which type-checked its arguments. – supercat Apr 12 '13 at 16:27
  • 4
    @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](https://github.com/dotnet/roslyn/issues/262). – Christopher Currens Oct 26 '15 at 21:22
  • 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.. – quetzalcoatl Aug 01 '17 at 00:07
  • 1
    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... – That Marc May 26 '19 at 15:54
  • 1
    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`). 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. – Jeppe Stig Nielsen May 22 '20 at 07:39
  • @JeppeStigNielsen but System.Enum inherits from System.ValueType, so the `struct` constraint adds nothing. Every type that inherits from System.Enum is non-nullable. This is either a bug or a design flaw. – phoog Jun 14 '22 at 20:28
  • @phoog It may feel like a flaw, but it is still true that it is better to say `where T : struct, System.Enum`, instead of just `where T : System.Enum`. Formally, the difference is only the type `System.Enum` **itself** which is a reference type. For example, the method at the top of this answer (as it stands right now) could be invoked with `EnumNamedValues();` and the constraint would be met, and `default(T)` (inside the method) would be a true null reference. – Jeppe Stig Nielsen Jun 15 '22 at 13:23
  • @JeppeStigNielsen of course. Thanks for setting me straight. Though by analogy with System.ValueType and `struct`, they could have used the `enum` keyword for the constraint. Do you know why they didn't? – phoog Jun 15 '22 at 15:31
  • @phoog No, I also wonder why not. It would have been nicer. – Jeppe Stig Nielsen Jun 15 '22 at 16:07
264

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.

Tohid
  • 6,175
  • 7
  • 51
  • 80
Julien Lebosquain
  • 40,639
  • 8
  • 105
  • 117
  • 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 where Enum : struct, IConvertible`? thanks. – David I. McIntosh Apr 11 '15 at 19:20
  • 9
    @DavidI.McIntosh `EnumClassUtils` 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 https://gist.github.com/MrJul/7da12f5f2d6c69f03d79 – Julien Lebosquain Apr 11 '15 at 20:51
  • 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 Apr 12 '15 at 01:00
  • 7
    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. – David I. McIntosh Apr 12 '15 at 15:08
  • 2
    Is there a way to work this to also be useable for extension methods? – Mord Zuber Apr 29 '15 at 15:11
  • @Max Unfortunately, I don't think so :( – Julien Lebosquain May 06 '15 at 13:07
  • 1
    I would add an internal constructor, to prevent this class from being inherited in strange ways. – John Gietzen May 09 '15 at 22:01
  • Wouldn't it be nice if you could do this: where T : class, struct – bubbleking Aug 07 '16 at 20:03
  • 3
    What does the `where TClass : class` constraint gain here? – tsemer Jan 04 '17 at 16:50
  • 2
    Is there a way to further constrain `TEnum` so that `int v; TEnum e = (TEnum) v;` is allowed? – Marc L. Jul 14 '17 at 12:30
  • 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! – Triynko May 15 '18 at 21:56
  • 2
    @Trinkyo `enum DefinitelyNotAnInt : byte { Realize, That, I, Am, Not, An, Int }` `enum AlsoNotAnInt : long { Well, Bummer }` – M.Stramm Dec 03 '18 at 12:00
  • 2
    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... – That Marc May 26 '19 at 15:56
  • @tsemer: The `where TClass : class` is needed to avoid that you can write `EnumUtils.Parse("value");` for example. – Andre Kampling Jul 12 '21 at 10:44
35

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;
    }
}
Community
  • 1
  • 1
Yahoo Serious
  • 3,728
  • 1
  • 33
  • 37
33

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;
}
DiskJunky
  • 4,750
  • 3
  • 37
  • 66
  • 2
    Interesting discussion there, thanks. Nothing set in stone yet though (as yet) – johnc Mar 27 '18 at 20:59
  • 1
    @johnc, very true but worth a note and it *is* a frequently asked feature. Fair odds on it coming in. – DiskJunky Mar 27 '18 at 21:00
  • 2
    This is coming in C# 7.3: https://learn.microsoft.com/en-us/visualstudio/releasenotes/vs2017-Preview-relnotes#csharp. :) – nawfal Apr 11 '18 at 17:46
  • 6
    Please upvote this answer, it should be much much higher in the list here, now that the feature exists! :) – Pac0 Jun 09 '20 at 12:59
20

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.

baumgarb
  • 1,955
  • 3
  • 19
  • 30
20

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.

Karg
  • 1,467
  • 3
  • 15
  • 18
  • 2
    This is a good option for generic classes -- but of course, it doesn't help for generic methods. – McGarnagle Feb 09 '15 at 19:18
  • 1
    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. – jrh Oct 09 '18 at 12:42
  • @McGarnagle a generic method (such as an extension method) can use a generic helper class to apply this pattern. This at least guarantees that the type will be tested only once. But of course it's still not enforced at compile time. – phoog Jun 14 '22 at 20:32
11

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;
}
nawfal
  • 70,104
  • 56
  • 326
  • 368
Martin
  • 1,028
  • 17
  • 23
  • 1
    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. – styfle Nov 02 '17 at 15:55
  • 1
    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](https://msdn.microsoft.com/en-us/library/system.enum.isdefined). – Nyerguds Mar 06 '18 at 15:03
11

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);
    }
}
Bivoauc
  • 853
  • 5
  • 10
  • 13
    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. – Mark Simpson Jun 25 '10 at 21:59
  • 1
    OP wants it to be case-insensitive, this is not. – Konrad Morawski Apr 04 '12 at 06:46
8

note that System.Enum Parse() & TryParse() methods still have where struct constraints rather than where Enum, so that this won't compile:

    bool IsValid<TE>(string attempted) where TE : Enum
    {
        return Enum.TryParse(attempted, out TE _);
    }

but this will:

bool Ok<TE>(string attempted) where TE : struct,Enum
{
    return Enum.TryParse(attempted, out var _)
}

as a result, where struct,Enum may be preferable to just where Enum

Chris F Carroll
  • 11,146
  • 3
  • 53
  • 61
6

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());
}
Sunny Rajwadi
  • 322
  • 1
  • 4
  • 12
6

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;
      }
}
nawfal
  • 70,104
  • 56
  • 326
  • 368
dimarzionist
  • 18,517
  • 4
  • 22
  • 23
  • 1
    If you need case insensitivity, simply replace `return (TValue)Enum.Parse(typeof (TValue), value);` by `return (TValue)Enum.Parse(typeof (TValue), value, true);` – Paulo Santos Jan 28 '10 at 10:04
3

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

Andrew
  • 8,322
  • 2
  • 47
  • 70
3

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

KarmaEDV
  • 1,631
  • 16
  • 27
  • 2
    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. – Nick Aug 28 '14 at 19:17
2

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>();
}
Jeff
  • 2,701
  • 2
  • 22
  • 35
2

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
Basheer AL-MOMANI
  • 14,473
  • 9
  • 96
  • 92
1

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;
}
expert
  • 29,290
  • 30
  • 110
  • 214
1

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);
        }
    }
}
niaher
  • 9,460
  • 7
  • 67
  • 86
1

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.

BatteryBackupUnit
  • 12,934
  • 1
  • 42
  • 68
1

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;
        }
    }
Cubelaster
  • 338
  • 4
  • 6
0

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);
uluorta
  • 133
  • 1
  • 8
  • 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()` – DiskJunky Mar 27 '18 at 21:17
-8

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;
    }


}
Rodney P. Barbati
  • 1,883
  • 24
  • 18