Список всех имен битов из флага Enum
Я пытаюсь создать вспомогательный метод для перечисления имен всех битов, заданных в значении Enum (для целей ведения журнала). Я хочу иметь метод, который возвращает список всех значений Enum, заданных в некоторых переменных. В моем примере
[Flag]
Enum HWResponse
{
None = 0x0,
Ready = 0x1,
Working = 0x2,
Error = 0x80,
}
Я кормлю его 0x81, и он должен предоставить мне IEnumerable<HWResponse>
, содержащий {Ready, Error}
.
Поскольку я не нашел более простой способ, я попытался написать код ниже, но я не могу его скомпилировать.
public static IEnumerable<T> MaskToList<T>(Enum mask)
{
if (typeof(T).IsSubclassOf(typeof(Enum)) == false)
throw new ArgumentException();
List<T> toreturn = new List<T>(100);
foreach(T curValueBit in Enum.GetValues(typeof (T)).Cast<T>())
{
Enum bit = ((Enum) curValueBit); // Here is the error
if (mask.HasFlag(bit))
toreturn.Add(curValueBit);
}
return toreturn;
}
В этой версии кода компилятор жалуется, что он не может отличать T от Enum.
Что я сделал неправильно? Есть ли лучший (более простой) способ сделать это? Как я могу сделать бросок?
Кроме того, я попытался написать метод как
public static IEnumerable<T> MaskToList<T>(Enum mask) where T:Enum
но Enum имеет особый тип, который запрещает синтаксис "where" (с использованием С# 4.0)
Ответы
Ответ 1
Вот простой способ записать его с помощью LINQ:
public static IEnumerable<T> MaskToList<T>(Enum mask)
{
if (typeof(T).IsSubclassOf(typeof(Enum)) == false)
throw new ArgumentException();
return Enum.GetValues(typeof(T))
.Cast<Enum>()
.Where(m => mask.HasFlag(m))
.Cast<T>();
}
Ответ 2
Если ваш желаемый конечный результат представляет собой строковый список имен, просто вызовите mask.ToString()
.
Что бы вы сделали, если перечисление было определено следующим образом:
[Flags]
enum State
{
Ready = 1,
Waiting = 2,
ReadyAndWaiting = 3
}
Что касается разрешения ошибки компилятора, это должно сделать это:
Enum bit = (Enum)(object)curValueBit;
У Jon Skeet есть проект под названием unconstrained melody, который позволяет вам добавить ограничение перечислимого числа после компиляции, переписав IL. Это работает, потому что CLR поддерживает такое ограничение, хотя С# этого не делает.
Еще одна мысль: будет эффективнее использовать возвращаемое значение GetValues непосредственно в T[]
:
foreach(T curValueBit in (T[])Enum.GetValues(typeof (T)))
Ответ 3
Основываясь на Ответ Gabe Я придумал это:
public static class EnumHelper<T>
where T : struct
{
// ReSharper disable StaticFieldInGenericType
private static readonly Enum[] Values;
// ReSharper restore StaticFieldInGenericType
private static readonly T DefaultValue;
static EnumHelper()
{
var type = typeof(T);
if (type.IsSubclassOf(typeof(Enum)) == false)
{
throw new ArgumentException();
}
Values = Enum.GetValues(type).Cast<Enum>().ToArray();
DefaultValue = default(T);
}
public static T[] MaskToList(Enum mask, bool ignoreDefault = true)
{
var q = Values.Where(mask.HasFlag);
if (ignoreDefault)
{
q = q.Where(v => !v.Equals(DefaultValue));
}
return q.Cast<T>().ToArray();
}
}
Я организовал вещи по-другому, а именно, я поставил проверку типа (то есть: подтверждение, что T действительно перечисление) и получение значений перечисления в статическом конструкторе, так что это делается только один раз (это было бы улучшение производительности).
Другое дело, я добавил необязательный параметр , чтобы вы могли игнорировать типичное значение "нуль" / "Нет" / "NotApplicable" / "Undefined" /etc для перечисления. p >
Ответ 4
Что делать, если что-то вроде этого:
public static IEnumerable<T> MaskToList<T>(Enum mask)
{
if (typeof(T).IsSubclassOf(typeof(Enum)) == false)
throw new ArgumentException();
List<T> toreturn = new List<T>(100);
foreach(T curValueBit in Enum.GetValues(typeof (T)).Cast<T>())
{
Enum bit = (curValueBit as Enum); // The only difference is actually here,
// use "as", instead of (Enum) cast
if (mask.HasFlag(bit))
toreturn.Add(curValueBit);
}
return toreturn;
}
Поскольку as
не выполняет проверку времени компиляции. Компилятор здесь просто "верит" вам, надеясь, что вы знаете, что делаете, поэтому ошибка времени компиляции не возникает.