Как я могу пометить целое число в общем списке 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);
}