53

I have regularly wondered why C# has not yet implemeted a Generic Enum.Parse

Lets say I have

enum MyEnum
{
   Value1,
   Value2
}

And from an XML file/DB entry I wish to to create an Enum.

MyEnum val = (MyEnum)Enum.Parse(typeof(MyEnum), "value1", true);

Could it not have been implemented as something like

MyEnum cal = Enum.Parse<MyEnum>("value1");

This might seem like a small issue, but it seems like an overlooked one.

Any thoughts?

0

7 Answers 7

48

It is already implemented in .NET 4 ;) Take a look here.

MyEnum cal;
if (!Enum.TryParse<MyEnum>("value1", out cal))
   throw new Exception("value1 is not valid member of enumeration MyEnum");

Also the discussion here contains some interesting points.

Sign up to request clarification or add additional context in comments.

7 Comments

That link is to the non-generic Enum.Parse method. Did you mean to link to the new Enum.TryParse<T> method? msdn.microsoft.com/en-us/library/…
It is interesting that they constrained it to struct, new() instead of adding a new enum constraint to the language.
The interesting thing is that they wouldn't even need to put a new constraint on the language - it would just be a method which couldn't actually be expressed in C#. The C# compiler can obey the constraints, even if you can't write them in C# :)
Too bad TryParse<T> is just as inconvenient as the non-generic Parse.
Heads up: with Enum.TryParse<T>, if you convert a string representation of an integer, i.e. "2", it always succeeds, regardless of any of the target enum members having an int value of 2. You'll have to subsequently call IsDefined on the result for complete validation.
|
21

And in the desired syntax of the question:

MyEnum cal = Toolkit.Parse<MyEnum>("value1");

Note: Since C# forbids you from adding static extensions, you have to house the function elsewhere. i use a static Toolkit class that contains all these useful bits:

/// <summary>
/// Converts the string representation of the name or numeric value of one or
//  more enumerated constants to an equivalent enumerated object.
/// </summary>
/// <typeparam name="TEnum">An enumeration type.</typeparam>
/// <param name="value">A string containing the name or value to convert.</param>
/// <returns>An object of type TEnum whose value is represented by value</returns>
/// <exception cref="System.ArgumentNullException">enumType or value is null.</exception>
/// <exception cref=" System.ArgumentException"> enumType is not an System.Enum. -or- 
/// value is either an empty string or only contains white space.-or- 
/// value is a name, but not one of the named constants defined for the enumeration.</exception>
/// <exception cref="System.OverflowException">value is outside the range of the underlying type of enumType.</exception>
public static TEnum Parse<TEnum>(String value) where TEnum : struct
{
   return (TEnum)Enum.Parse(typeof(TEnum), value);
}

Comments

10

The generic version of Parse<TEnum>(String) was introduced in .NET Core 2.0. So you can just write:

using System;

var e = Enum.Parse<MyEnum>("Value1");
Console.WriteLine($"Enum values is: {e}");

enum MyEnum
{
    Value1,
    Value2
}

Just keep in mind this is not in "old" .Net Framework (.NET 4.8 and less) or in any .NET Standard. You need to target .NET Core >= 2 (or .NET >= 5 since Microsoft dropped "Core" naming).

There is also a generic version of TryParse<TEnum>(String, TEnum) since .NET Framework 4.0. So you can use it like that:

if (Enum.TryParse<MyEnum>("Value2", out var e2))
{
    Console.WriteLine($"Enum values is: {e2}");
}

or create your own helper method like:

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

...
var e3 = EnumUtils.Parse<MyEnum>("Value1");

and of course you can just use non-generic version until you migrate your project to newer .NET ;)

var e4 = (MyEnum)Enum.Parse(typeof(MyEnum), "Value1");

Comments

8

Although constraining to System.Enum isn't allowed by C#, it is allowed in .NET and C# can use types or methods with such constraints. See Jon Skeet's Unconstrained Melody library, which includes code that does exactly what you want.

1 Comment

Humbug, I can't even plug my own library without someone getting there first ;)
3
public class EnumHelper
{
    public static T? TryParse<T>(string text)
        where T: struct
    {
        if (string.IsNullOrEmpty(text))
        {
            return null;
        }

        T r;

        if (Enum.TryParse<T>(text, out r))
        {
            return r;
        }

        return null;
    }
}

Comments

2

Slightly modified version of @ian-boyd's answer, using an extension method to avoid the need to specify a static class name in the call:

MyEnum cal = "value1".Parse<MyEnum>();

/// <summary>
/// Converts the string representation of the name or numeric value of one or
//  more enumerated constants to an equivalent enumerated object.
/// </summary>
/// <typeparam name="TEnum">An enumeration type.</typeparam>
/// <returns>An object of type TEnum whose value is represented by value</returns>
/// <exception cref="System.ArgumentNullException">enumType or value is null.</exception>
/// <exception cref=" System.ArgumentException"> enumType is not an System.Enum. -or- 
/// value is either an empty string or only contains white space.-or- 
/// value is a name, but not one of the named constants defined for the enumeration.</exception>
/// <exception cref="System.OverflowException">value is outside the range of the underlying type of enumType.</exception>
public static TEnum Parse<TEnum>(this String value) where TEnum : struct
{
   return (TEnum)Enum.Parse(typeof(TEnum), value);
}

Comments

0

While tweaking a little with some methods, trying to build something similar to the initial proposal:

MyEnum cal = Enum.Parse<MyEnum>("value1");

it seemed to me that this syntax won´t be possible in C#, since the Enum type is treated as non-nullable.

If we call the "Enum.TryParse" method passing a value not corresponding to an item of the enum, the Enum´s default value will be returned in the "out" variable. That´s why we need to test the "Enum.TryParse" result first, since simply calling

MyEnum cal;
Enum.TryParse<MyEnum>("value1", out cal);

and checking "cal" value will not always give a reliable result.

3 Comments

the generic Parse could simply be a wrapper around TryParse and throw an ArgumentException if TryParse returns false.
It can simply raise exception instead of default value. The same as non-generic version of Parse.
The idea is to avoid (when possible) exception handling, which is considered an anti-pattern for flow control (ref. web.archive.org/web/20140430044213/http://c2.com/cgi-bin/…). According to its API specification, the Enum.TryParse method "eliminates the need for exception handling when parsing the string representation of an enumeration value." (ref. msdn.microsoft.com/en-us//library/dd991317(v=vs.110).aspx)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.