It seems that enums might be the neglected step-child when it comes to Generics and Extensions.  I've tried defining generics that are constrained to enums.  No luck.  I faced much frustration trying to get my generic methods to work.  I finally came up with a method that meets my need.  But, I had to add a little bit of run-time type checking.

Instead of repeating Enum.Parse throughout my code, I wanted to create a simple generic extension method that would do it for me.

I just discovered that my extension is practically identical to Kirill Osenkov'sCheck out his discussion here.

   1: public static TResult ConvertTo<TResult>( this string source )
   2: {
   3:     if( !typeof(TResult).IsEnum )
   4:     {
   5:         throw new NotSupportedException( "TResult must be an Enum" );
   6:     }
   7:     return (TResult)Enum.Parse( typeof(TResult), source );
   8: }

If the compile time constraint were allowed, I would prefer this

   1: public static TResult ConvertTo<TResult>( this string source ) where TResult : Enum
   2: {
   3:     return (TResult)Enum.Parse( typeof(TResult), source );
   4: }

The usage is something like this.

   1: MyObject.ClosingStatus = input.ConvertTo<ClosingStatus>();
   2: //or
   3: reader.GetString( 2 ).ConvertTo<ClosingStatus>();

After I came up with this, I searched to find out if anyone else out there found a better way to resolve the limitations surrounding enums.  I found Christopher Bennage's post on Enum<T> and I like it quite a bit.

posted on Saturday, June 28, 2008 11:10 PM
Filed Under [ .Net Design C# ]

Comments

Gravatar
# re: C# Generics for Enums
on 6/29/2008 10:53 PM
I'm glad you liked it, and I am in complete agreement about being able to constrain generics to enums. :-)
Gravatar
# re: C# Generics for Enums
on 9/25/2009 10:32 AM
Thanks! That was helpful. I've exteded your idea in a more forgiving implementation:

<br>
<pre>

public static T GetEnumValueFromString<T>(string enumValueString, T defaulEnumValue)
{
T returnValue = defaulEnumValue;
if (typeof (T).IsEnum)
{
bool found = false;
if (new ArrayList(Enum.GetNames(typeof(T))).Contains(enumValueString))
{
returnValue = (T) Enum.Parse(typeof (T), enumValueString);
found = true;
}
else
{
string search = (enumValueString + "").ToUpper().Replace(" ", "").Replace("-", "");
FieldInfo[] fieldInfoList = typeof (T).GetFields(
BindingFlags.Public | BindingFlags.Static |
BindingFlags.FlattenHierarchy);

foreach (FieldInfo fi in fieldInfoList)
{
if ((fi.Name + ".").ToUpper().Contains(search))
{
returnValue = (T) fi.GetValue(typeof (T));
found = true;
break;
}
}
}
if (!found)
{
throw new NotSupportedException( String.Format( "Cannot cast string \"{0}\" to an enum value of type {1}.",
enumValueString, typeof(T).FullName) );
}
}
return returnValue;
}

</pre>
Gravatar
# re: C# Generics for Enums
on 9/25/2009 1:19 PM
Oops, to trap null and empty tring, better change the 1st if statement to:

if (typeof(T).IsEnum && !String.IsNullOrEmpty(enumValueString))
Gravatar
# re: C# Generics for Enums
on 9/25/2009 4:54 PM
Double oops... that was for constants rather than enums.

PLEASE DISREGARD
Gravatar
# re: C# Generics for Enums
posted by Bas Mommenhof
on 10/14/2009 8:09 PM
Well, here's a tip for you.
Enums are structs!
And you can use a struct in your constraint. Also the typeparameter is not limited to a single character.

my revised version would look like this:
public static TEnumType GetEnumValueFromString<TEnumType>(string enumValueString, TEnumType defaulEnumValue) where TEnumType: struct

We have now resticted the parameters that may be passed, and by the name of the typeparameter it is clear that we only want Enums.

You can define the constraint on all your generic code, which would provide a lot of cmpile time checking.
Gravatar
# re: C# Generics for Enums
posted by Bas
on 10/14/2009 8:54 PM
Secondly I am not quite sure what you are trying to achieve, if the point is to create an case-insensitive search, why not use

(T) Enum.Parse(typeof (T), enumValueString, true);

Note the second parameter.

Alternatly I found a way to compare the values of two enumerator values when one of them is a generic. I am stayng away from stringconversions because the name of an ENum is more likely to change than its' numeric value:

public bool Equals<TEnumType>(TEnumType value, KindOfEnum compareTo)
where TEnumType: struct
{
//need to cast it to enum first, direct cast won't compile
Enum castAsEnum = value as Enum;
//This cast will result in an nonexisting value if KindOfEnum does not have
//a matching value for the TEnumType value.
//Since I only want to know it they are equal, I do not care.
KindOfEnum castAsDesiredType = (KindOfEnum)castAsEnum;
//compare the values.
return (castAsDesiredType == compareTo);
}
Gravatar
# re: C# Generics for Enums
posted by GI
on 12/20/2009 6:20 AM
Enums are also IConvertible, if you would like to further constrain your generic type.

Here is an example from a snippet which converts between two kinds of enums with the same values. ( No checks performed )



public class EnumConverter<sourceEnum,targetEnum> where sourceEnum:struct, IConvertible
where targetEnum:struct, IConvertible
{
public static targetEnum convert(sourceEnum source)
{

return (targetEnum) Enum.ToObject(typeof(targetEnum),source.ToInt32(null));
}
}

Post A Comment
Title:
Name:
Email:
Comment:
Verification: