0

I've search for a solution for my problem but so far i was not able to figure out how to do it.

I need to get a list of values of an enum flagged by a int value.
I was able to do it in a specific case, but I want to create a generic function for a generic enumerator.

The code for the specicic case is:

        IList<MyEnum> EnumList= new List<MyEnum>();
        MyEnum EnumIUseToFlag= (MyEnum)intToParse;

        foreach (MyEnumitem in Enum.GetValues(typeof(MyEnum)))
        {
            if (EnumIUseToFlag.HasFlag(item))
            {
                EnumList.Add(item);
            }
        }

Now, for the generic method i was trying something like:

    public static IList<T> GetFlaggedValues<T>(int intValue) where T : struct, IConvertible
    {

        if (!typeof(T).IsEnum)
        {
            throw new ArgumentException("T must be an enumerated type");
        }

        IList<T> listToReturn = new List<T>();
        Enum enumToParse = Enum.Parse(typeof(T), intValue.ToString()) as Enum;
        foreach (T item in Enum.GetValues(typeof(T)))
        {
            // here I am not able to cast item in the integer
            //values i need to use in order to flag my enum

        }

        return listToReturn;
    }
newpeople
  • 336
  • 2
  • 12

3 Answers3

2

Simple answer is-- don't use generics. Not needed for this problem. Use good old inheritance and pass the flag as a System.Enum, which is the base class for all enums.

public static IList<Enum> GetFlaggedValues(Enum flag) 
{
    return Enum
        .GetValues(flag.GetType())
        .Cast<Enum>()
        .Where(e => e.HasFlag(flag))
        .ToList();
}

Here's a simple test:

enum MyEnum
{
    Red = 0xF00,
    Orange = 0xFF0,
    Yellow = 0x0F0,
    Green = 0x0FF,
    Blue = 0x00F,
    Purple = 0xF0F
}

public static void Main()
{
    var list = GetFlaggedValues(MyEnum.Blue);
    foreach (var e in list)
    {
        Console.WriteLine(e);
    }
}

Output:

Blue
Green
Purple

Due to the way we are using the Enum base type, it is impossible to call this method with a non-enum argument, so no need to have a messy validation check and throw an exception. Can't do that with generics in c#.

On the other hand, maybe it really bothers you that the return type is List<Enum> and not List<MyEnum>. Fine. If you insist on a strongly-typed list for a resultset, you can just cast back and forth, like this:

public static IList<T> GetFlaggedValues<T>(T input) where T : struct, IConvertible
{
    Enum flag = input as Enum;
    if (flag == null) return new List<T>();
    return Enum
        .GetValues(typeof(T))
        .Cast<Enum>()
        .Where(e => e.HasFlag(flag))
        .Cast<T>()
        .ToList();
}

Note in this last example, if the input isn't actually an enum, I just return an empty list, which seems like reasonable behavior since it accurately reflects which values in the enumeration match-- none of them. But you can throw an exception if you prefer.

Also, you may have noticed that I changed your prototype so that you can pass the enum itself rather than an integer. This allows the compiler to infer the type, so you can just call

var list = GetFlaggedValues(MyEnum.Blue);

instead of having to type

var list = GetFlaggedValues<MyEnum>((int)MyEnum.Blue);  //Ugly

If you want to pass an integer, you can always cast it, like this:

var list = GetFlaggedValues((MyEnum)0xF0F);

...and the compiler will infer the <MyEnum> bit.

Full code on DotNetFiddle

John Wu
  • 50,556
  • 8
  • 44
  • 80
0

This will work for you:

    public static IList<T> GetFlaggedValues<T>(ulong intValue) where T : struct
    {
        if (!typeof(T).IsEnum)
        {
            throw new ArgumentException("T must be an enumerated type");
        }
        var flagAttrib = Attribute.GetCustomAttribute(typeof(T), typeof(FlagsAttribute));
        if(flagAttrib==null)
        {
            throw new ArgumentException("T must have [Flags] attribute.");
        }

        List<T> vals = new List<T>();
        foreach (var e in Enum.GetValues(typeof(T)))
        {
            var item = Convert.ToUInt64(e);
            if ((item & intValue) != 0)
                vals.Add((T)Enum.ToObject(typeof(T), item));
        }
        return vals;
    }
MineR
  • 2,144
  • 12
  • 18
0

Inside foreach you can just add next lines:

        var itemAsEnumValue = (Enum)Enum.Parse(typeof(T), item.ToString());

        if (enumToParse.HasFlag(itemAsEnumValue))
        {
            listToReturn.Add(item);
        }

Here is working .netfiddle example