BorjeNorden
BorjeNorden

Reputation: 11

Constraining a generic method using Enum methods to T

I've been trying to write a utility extension method for flagged enum values. The purpose with this method is to retrieve a list of all the flags that are currently enabled.

What I wanted to do was this:

 public static IEnumerable<T> GetFlags<T>(this T value) where T:Enum 
  {
     return Enum.GetValues(typeof(T)).OfType<Enum>().Where(value.HasFlag).Cast<T>(); 
  }

However, since generics doesn't work with the Enum type I had to resort to the following:

public static IEnumerable<T> GetFlags<T>(this Enum value) 
  {
     return Enum.GetValues(typeof(T)).OfType<Enum>().Where(value.HasFlag).Cast<T>(); 
  }

Is there any way to get around this or should I resign myself to explicitly having to declare the type every time the method is called ?

Upvotes: 1

Views: 1007

Answers (2)

remio
remio

Reputation: 1280

I use reflection. Just invoke the HasFlag (or whatever method you need) by yourself at runtime.

The where clause gives as much constraint as we can. Then, if your type is not an enum, this code will raise a TargetInvocationException.

/// <summary>
/// Gets the flags for the given enum value.
/// </summary>
/// <typeparam name="T">The enum type</typeparam>
/// <param name="enumValue">The enum value.</param>
/// <returns>The flagged values.</returns>
public static IEnumerable<T> GetFlags<T>(this T enumValue)
    where T : struct
{
    Type enumType = enumValue.GetType();
    foreach (T value in Enum.GetValues(enumType))
    {
        bool hasFlag = (bool)enumType.InvokeMember("HasFlag", BindingFlags.InvokeMethod, null, enumValue, new object[] { value });
        if (hasFlag)
        {
            yield return value;
        }
    }
}

Upvotes: 0

SLaks
SLaks

Reputation: 887777

The best you can do is where T : struct.

For non-extension methods, you can use an ugly trick:

public abstract class Enums<Temp> where Temp : class {
    public static TEnum Parse<TEnum>(string name) where TEnum : struct, Temp {
        return (TEnum)Enum.Parse(typeof(TEnum), name); 
    }
}
public abstract class Enums : Enums<Enum> { }

Enums.Parse<DateTimeKind>("Local")

If you want to, you can give Enums<Temp> a private constructor and a public nested abstract inherited class with Temp as Enum, to prevent inherited versions for non-enums.

You can't use this trick to make extension methods because extension methods must be defined in a static class and cannot inherit a different class.

Upvotes: 2

Related Questions