Как я могу пометить целое число в общем списке enum?

Я ищу решение для своей проблемы, но до сих пор не смог понять, как это сделать.

Мне нужно получить список значений enum помечены int значение.
Я смог сделать это в конкретном случае, но я хочу создать универсальную функцию для универсального перечислителя.

Код для конкретного случая:

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

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

Теперь для универсального метода я пробовал что-то вроде:

    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;
    }

3 ответа

Простой ответ - не используйте дженерики. Не нужно для этой проблемы. Используйте старое доброе наследство и передайте флаг как System.Enum, который является базовым классом для всех перечислений.

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

Вот простой тест:

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);
    }
}

Выход:

Blue
Green
Purple

Из-за того, как мы используем Enum базовый тип, этот метод нельзя вызывать с аргументом, отличным от enum, поэтому нет необходимости иметь грязную проверку правильности и генерировать исключение. Не могу сделать это с генериками в C#.

С другой стороны, может быть, вас действительно беспокоит, что тип возвращаемого значения List<Enum> и не List<MyEnum>, Хорошо. Если вы настаиваете на строго типизированном списке для набора результатов, вы можете просто приводить туда-сюда, например так:

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();
}

Обратите внимание, что в этом последнем примере, если входные данные на самом деле не являются перечислением, я просто возвращаю пустой список, который выглядит как разумное поведение, поскольку он точно отражает, какие значения в перечислении совпадают - ни одно из них. Но вы можете бросить исключение, если вы предпочитаете.

Также вы, возможно, заметили, что я изменил ваш прототип, чтобы вы могли передавать само перечисление, а не целое число. Это позволяет компилятору выводить тип, так что вы можете просто вызвать

var list = GetFlaggedValues(MyEnum.Blue);

вместо того, чтобы печатать

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

Если вы хотите передать целое число, вы всегда можете разыграть его следующим образом:

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

... и компилятор выведет <MyEnum> немного.

Полный код на DotNetFiddle

Это будет работать для вас:

    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;
    }

Внутри foreach вы можете просто добавить следующие строки:

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

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

Вот рабочий пример.netfiddle

Другие вопросы по тегам