43

I'm trying to build generic function that get from user string and try to parse it to Enum valuse like this:

private Enum getEnumStringEnumType(Type i_EnumType)
    {
        string userInputString = string.Empty;
        Enum resultInputType;
        bool enumParseResult = false;

        while (!enumParseResult)
        {                
            userInputString = System.Console.ReadLine();
            enumParseResult = Enum.TryParse(userInputString, true, out resultInputType);
        }
    }

But i get:

The type 'System.Enum' must be a non-nullable value type in order to use it as parameter 'TEnum' in the generic type or method 'System.Enum.TryParse<TEnum>(string, bool, out TEnum)    .

The Error means that i need to decalare a specific Enum for resultInputType? How can I fix this ? Thanks.

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Rami
  • 2,098
  • 5
  • 25
  • 37
  • When you say "generic function" - your method *isn't* generic. Do you need to be able to specify the type as a `Type` value rather than making this a true generic method? – Jon Skeet May 21 '12 at 13:07

7 Answers7

72

The TryParse method has the following signature:

TryParse<TEnum>(string value, bool ignoreCase, out TEnum result)
    where TEnum : struct

It has a generic type parameter TEnum that must be a struct and that is used to determine the type of enumeration being parsed. When you don't provide it explicitly (as you did), it will take the type of whatever you provide as the result argument, which in your case is of type Enum (and not the type of the enumeration itself).

Note that Enum is a class (despite it inheriting from ValueType) and therefore it does not satisfy the requirement that TEnum is a struct.

You can solve this by removing the Type parameter and giving the method a generic type parameter with the same constraints (i.e. struct) as the generic type parameter on the TryParse function.

So try this, where I've named the generic type parameter TEnum:

private static TEnum GetEnumStringEnumType<TEnum>()
    where TEnum : struct
{
    string userInputString = string.Empty;
    TEnum resultInputType = default(TEnum);
    bool enumParseResult = false;

    while (!enumParseResult)
    {                
        userInputString = System.Console.ReadLine();
        enumParseResult = Enum.TryParse(userInputString, true, out resultInputType);
    }
    return resultInputType;
}

To call the method, use:

GetEnumStringEnumType<MyEnum>();
Daniel A.A. Pelsmaeker
  • 47,471
  • 20
  • 111
  • 157
  • Enum.TryParse is generic... http://msdn.microsoft.com/en-us/library/system.enum.aspx – eyossi May 21 '12 at 13:13
  • That's correct. I never said it wasn't. Its signature is `TryParse(string value, bool ignoreCase, out TEnum result) where TEnum : struct, new()`, and if you specify `result` it will use the type of that variable to determine `TEnum`, saving you from specifying it explicitly. – Daniel A.A. Pelsmaeker May 21 '12 at 13:17
  • 3
    I get the same error: The type 'TEnum' must be a non-nullable value type in order to use it as parameter 'TEnum' in the generic type or method 'System.Enum.TryParse(string, bool, out TEnum). – Rami May 21 '12 at 13:35
  • My error when doing this is "The 'new()' constraint cannot be used with the 'struct' constraint" – PandaWood Nov 09 '16 at 04:32
  • 1
    @PandaWood I believe `new()` used to be redundant, but now it seems to be forbidden. I updated the answer. – Daniel A.A. Pelsmaeker Nov 09 '16 at 09:55
  • Very nice answer! – Law Jan 25 '17 at 23:13
  • I've checked that constraint may be `where TEnum : struct, Enum` I think it perfect option when you want to restrict access for non-enum structs. – 3per Jan 24 '20 at 05:54
7

You should make a generic method:

private T getEnumStringEnumType<T>() where T : struct, IConvertible
    {
        string userInputString = string.Empty;
        T resultInputType = default(T);
        bool enumParseResult = false;

        while (!enumParseResult)
        {
            userInputString = System.Console.ReadLine();
            enumParseResult = Enum.TryParse<T>(userInputString, out resultInputType);
        }

        return resultInputType;
    }

usage:

public enum myEnum { val1, val2 }

myEnum enumValue = getEnumStringEnumType<myEnum>();
eyossi
  • 4,230
  • 22
  • 20
  • I get the same erroe: The type 'T' must be a non-nullable value type in order to use it as parameter 'TEnum' in the generic type or method 'System.Enum.TryParse(string, out TEnum); – Rami May 21 '12 at 13:38
  • follow the updated code (i added where T : struct, new()). and look at the usage sample – eyossi May 21 '12 at 13:43
  • You are right... i fixed it now and it works. For the enum constraint i used that - http://stackoverflow.com/questions/79126/create-generic-method-constraining-t-to-an-enum – eyossi May 21 '12 at 14:37
7

Long ago in Visual Studio 2005 era, I made my own method for TryParse on Enum. I only recently discovered the 2008 implementation and I'm not happy with it's restrictiveness, especially considering that it's a TRY PARSE method; meaning that a programmer is testing an input!

Generally, I prefer to use methods which trust the programmer to know what he's doing :)

My implementation is as follows:

public static bool EnumTryParse<T>(string input, out T theEnum)
{
    foreach (string en in Enum.GetNames(typeof(T)))
    {
        if (en.Equals(input, StringComparison.CurrentCultureIgnoreCase))
        {
            theEnum = (T)Enum.Parse(typeof(T), input, true);
            return true;
        }
    }

    theEnum = default(T);
    return false;
}

The lack of a where T:struct puts the trust in the developer's hands, but it allows you to compile with unknown, generic enums.

As an alternative, you can create a method looping on Enum.GetValues if you want to do an integer comparison when converting to your specified enum.

Hope this helps.

Mark Mintoff
  • 282
  • 3
  • 5
3

As of C# 7 we can specify a generic type as an Enum

where TCustomValidatorsEnum : Enum

As commented

TryParse<TEnum>(string value, bool ignoreCase, out TEnum result)
    where TEnum : struct

Will compile-complain as presented in the OP:

The type 'System.Enum' must be a non-nullable value type in order to use it as parameter 'TEnum' in the generic type or method 'System.Enum.TryParse(string, bool, out TEnum)

This version does compile though:

var converted = Enum.TryParse(typeof(TCustomValidatorsEnum), schemaConstraintType, true, out var customValidationEnum);
jenson-button-event
  • 18,101
  • 11
  • 89
  • 155
  • 1
    The _correct_ way ([like Microsoft itself is doing](https://github.com/dotnet/runtime/blob/7d2a4d298a3d9bed50089eb79f67e5e8f3b0b190/src/libraries/System.Private.CoreLib/src/System/Enum.cs#L58)) would actually be `where TCustomValidatorsEnum : struct, Enum`. then the warnings go away and you still properly constrain the type. – JHBonarius Jun 23 '23 at 08:50
1

Enum.TryParse is a generic method, which means that its generic type parameters have to be known at compile time. This in turn means that yes, you do have to declare resultInputType as a specific enum type for the code to compile.

If you think on it, the original code is a bit too optimistic: nowhere does it say which enum type should be checked for a member with name equal to userInputString. How could TryParse work without this information?

Jon
  • 428,835
  • 81
  • 738
  • 806
  • 1
    This sucks. The generic type constraints make it impossible to implement a method that checks whether the generic type T is an enum type and parse it. For example: `if (typeof(T).IsEnum) { if (Enum.TryParse(s, true, out T outputValue)) return outputValue; else throw new Exception("Parse failed."); } else { return Convert.ChangeType(s, typeof(T)); }` That code will fail to compile, because the TryParse method requires T to be a struct. Furthermore, while Enum.Parse accepts a 'type' instance, Enum.TryParse has no such overload and only has that over-constrained generic implementation. – Triynko Mar 04 '19 at 19:25
0

String Extension Method

public TEnum ToEnum<TEnum>(this string value, TEnum defaultValue){

if (string.IsNullOrEmpty(value))return defaultValue;

return Enum.Parse(typeof(TEnum), value, true);}
AHMED RABEE
  • 467
  • 6
  • 13
0
    /// <summary>
    ///     Usage:
    ///         yourEnum = yourString.ToEnum<TEnum>()
    /// </summary>
    /// 
    /// <Parameters>
    ///     <typeparam name="TEnum"></typeparam>
    ///     <param name="value"></param>
    /// </Parameters>
    /// 
    /// <returns>
    ///     (TEnum) parsed
    /// </returns>
    /// 
    public static TEnum ToEnum<TEnum>(this string value)
    {

        var parsed = Enum.Parse(typeof(TEnum), value, true);

        return (TEnum)parsed;
    }