views:

1264

answers:

4

I've added some extension methods for strings to make working with some custom enums easier.

public static Enum ToEnum<T>(this string s)
{
    return (Enum)Enum.Parse(typeof(T), s);
}

public static bool IsEnum<T>(this string s)
{
    return Enum.IsDefined(typeof(T), s);
}

Note -- because of limitations of generic type constraints, I have to write the methods like the above. I would love to use T ToEnum(this string s) where T: Enum to avoid the cast after making the call ... but no can do.

In any event, I figured it would be nice to extend this concept a little bit to return Enum? in those instances where a method signature can accept various nullable enums.

public static Enum? ToEnumSafe<T>(this string s)
{
    return (IsEnum<T>(s) ? (Enum)Enum.Parse(typeof(T), s) : null);
}

However, this is a no-go due to compiler errors.

error CS0453: The type 'System.Enum' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'System.Nullable<T>'

I have to admit I'm a bit confused here as Enum? should be a legitimate return value, no?. I tried something similar, but end up with the same error.

public static T? ToEnumSafe<T>(this string s)
{
    return (IsEnum<T>(s) ? (T)Enum.Parse(typeof(T), s) : null);
}

I even decided to rewrite the methods to remove the generics and I get more of the same:

public static bool IsEnum(this string s, Type T)
{
    return Enum.IsDefined(T, s);
}
public static Enum? ToEnumSafe(this string s, Type T)
{
    return (IsEnum(s, T) ? (Enum)Enum.Parse(T, s) : null);
}

Am I missing something really stupid here?

A: 
public static Enum ToEnum<T>(this string s)
{
    return (Enum)Enum.Parse(typeof(T), s);
}

should be

public static T ToEnum<T>(this string s)
{
    return (T)Enum.Parse(typeof(T), s);
}

can also fix the following

public static Enum? ToEnumSafe<T>(this string s)
{
    return (IsEnum<T>(s) ? (Enum)Enum.Parse(typeof(T), s) : null);
}

to

public static T? ToEnumSafe<T>(this string s)
    where T : struct
{
    return (IsEnum<T>(s) ? (T?)Enum.Parse(typeof(T), s) : null);
}
Dave
`return (IsEnum<T>(s) ? (T)Enum.Parse(typeof(T), s) : null);` won't compile.
Mehrdad Afshari
shoot missed a '?'
Dave
+3  A: 

Try:

public static T? ToEnumSafe<T>(this string s) where T : struct
{
    return (IsEnum<T>(s) ? (T?)Enum.Parse(typeof(T), s) : null);
}
Mehrdad Afshari
+2  A: 

You can actually do somewhat better than this - with a bit of work.

Although C# doesn't support generic constraints to say that T must be an enum type, the CLR does. I have a project called Unconstrained Melody which is a library of "useful things to do with an enum." I suspect it already copes with what you want (so long as you only need to use the names in the enum, not the string representations of the integer values). Although it doesn't have IsDefined(string), it does have TryParse which will do the same job.

See this blog post for more details.

As to why Enum? isn't a valid return type - System.Enum itself is a reference type (just like System.ValueType), so it's already nullable. You can only use ? with non-nullable value types.

Jon Skeet
I was googling to link to UnconstrainedMelody. Fortunately, you came around just at the right time :)
Mehrdad Afshari
Awesome... this is a library that I hadn't come across. Will be checking it out.
Ethan J. Brown
A: 

An Enum is no distinct Type in .Net it is just a placeholder for a concrete value type (int in case you didn't specify otherwise).

That means you cant have it as type declaration at all (regardless of static extention method or not).

Foxfire
Yes you can. System.Enum is a valid type in itself - but it's a reference type rather than a value type, hence "Enum?" doesn't work.
Jon Skeet
Well lets say you can't have it as a type for the purpose the original author wanted it to have. Because other than the stuff it inherits from ValueType and its interfaces it has no own non-static methods or properties.
Foxfire