Получить список <string> моих атрибутов enum с помощью общего метода
В начале мы имеем это базовое перечисление.
public enum E_Levels {
[ValueOfEnum("Low level")]
LOW,
[ValueOfEnum("Normal level")]
NORMAL,
[ValueOfEnum("High level")]
HIGH
}
И я хотел бы получить List<string>
все перечисление. Что-то вроде Extensions.GetValuesOfEnum<E_Levels>()
, которое могло бы вернуть List<string>
с "низким уровнем", "нормальным уровнем" и "высоким уровнем" в нем.
StackOF помог мне получить атрибут value one:
public static class Extensions {
public static string ToValueOfEnum(this Enum value) {
FieldInfo fieldInfo = value.GetType().GetField(value.ToString());
ValueOfEnum[] attribs = fieldInfo.GetCustomAttributes(typeof(ValueOfEnum), false) as ValueOfEnum[];
return attribs.Length > 0 ? attribs[0].value : null;
}
}
И я могу вызвать этот метод независимо от перечисления: E_Levels.LOW.ToValueOfEnum()
.
Кроме того, StackOF помог мне получить List<string>
для конкретного перечисления.
Я сделал этот метод в контроллере:
private List<string> GetLevels() {
List<string> levelsToReturn = new List<string>();
var levels = Enum.GetValues(typeof(E_Levels)).Cast<E_Levels>();
foreach(E_Levels l in levels)
levelsToReturn.Add(l.ToValueOfEnum());
return levelsToReturn;
}
Но этот способ требует, чтобы я переписал один и тот же метод для каждого перечисления.
Поэтому я попытался добавить этот общий метод в мои расширения класса:
public static class Extensions {
public static string ToValueOfEnum(this Enum value) {...}
public static List<string> GetValuesOf<T>() {
List<string> levelsToReturn = new List<string>();
var levels = Enum.GetValues(typeof(T)).Cast<T>();
foreach(T l in levels)
levelsToReturn.Add(l.ToValueOfEnum());
return levelsToReturn;
}
}
Но в моем foreach .ToValueOfEnum()
- неизвестный метод.
Так что у меня проблемы, я надеялся, что смогу найти способ не переписывать снова и снова тот же метод для каждого перечисления...
Ответы
Ответ 1
Попытайтесь сохранить это более общее назначение.
У меня есть метод расширения , который может захватывать атрибуты значений enum. Это даст вам быстрый доступ к атрибутам.
public static class EnumExtensions
{
public static TAttribute GetAttribute<TAttribute>(this Enum value)
where TAttribute : Attribute
{
var type = value.GetType();
var name = Enum.GetName(type, value);
return type.GetField(name)
.GetCustomAttributes(false)
.OfType<TAttribute>()
.SingleOrDefault();
}
}
Используя это, вы можете создать несколько запросов, чтобы получить то, что вы хотите.
var valuesOfLevels =
Enum.GetValues(typeof(E_Levels)).Cast<E_Levels>()
.Select(level => level.GetAttribute<ValueOfEnumAttribute>().Value);
Таким образом, ваш метод GetValuesOf()
(который не является отличным именем для такого метода специальности IMHO) можно записать следующим образом:
public static List<string> GetValuesOf<TEnum>()
where TEnum : struct // can't constrain to enums so closest thing
{
return Enum.GetValues(typeof(TEnum)).Cast<Enum>()
.Select(val => val.GetAttribute<ValueOfEnumAttribute>().Value)
.ToList();
}
Теперь вы можете вызвать метод следующим образом:
var levelValues = GetValueOf<E_Levels>();
// levelValues = { "Low level", "Normal level", "High level" }
Ответ 2
Вы можете попробовать выполнить (Enum)(object)l
, изменив ToValueOfEnum
, чтобы взять object
, или просто встроить метод:
public static List<string> GetValuesOf<T>()
{
List<string> levelsToReturn = new List<string>();
var levels = Enum.GetValues(typeof(T)).Cast<T>();
foreach (T value in levels)
{
FieldInfo fieldInfo = value.GetType().GetField(value.ToString());
ValueOfEnum[] attribs = fieldInfo.GetCustomAttributes(typeof(ValueOfEnum), false) as ValueOfEnum[];
levelsToReturn.Add(attribs.Length > 0 ? attribs[0].value : null);
}
return levelsToReturn;
}
Здесь однострочное решение с использованием метода литья:
return new List<string>(Enum.GetValues(typeof(T)).Cast<Enum>().Select(x => x.ToValueOfEnum()));
Если вам не ясно, почему T
не был распознан как Enum
, например E_Levels
, это потому, что вы не указали это T : enum
. К сожалению, вы не можете указать, что в С# (хотя CLR поддерживает его), поэтому другие подходы, такие как проверка/принятие во время выполнения (например, что я предлагаю здесь) или модификации кода после компиляции (например, unconstrained-melody).
Ответ 3
.Net уже имеет тот же атрибут Description
, поэтому вы можете использовать его вместо ValueOfEnum
.
Как насчет dynamic
и расширения на type
и следующем примере
[TestFixture]
public sealed class ForTest
{
[Test]
public void Test()
{
var values = typeof(Levels).ToValues();
values.ForEach(Console.WriteLine);
}
}
public static class TypeExtensions
{
public static List<string> ToValues(this Type value)
{
var result = new List<string>();
var values = ToConcreteValues(value);
foreach (dynamic item in values)
{
Description attribute = GetAttribute<Description>(item);
result.Add(attribute.Description);
}
return result;
}
private static dynamic ToConcreteValues(Type enumType)
{
Array values = Enum.GetValues(enumType);
Type list = typeof (List<>);
Type resultType = list.MakeGenericType(enumType);
dynamic result = Activator.CreateInstance(resultType);
foreach (object value in values)
{
dynamic concreteValue = Enum.Parse(enumType, value.ToString());
result.Add(concreteValue);
}
return result;
}
private static TAttribute GetAttribute<TAttribute>(dynamic value)
where TAttribute : Attribute
{
Type type = value.GetType();
FieldInfo fieldInfo = type.GetField(Enum.GetName(type, value));
return (TAttribute) Attribute.GetCustomAttribute(fieldInfo, typeof (TAttribute));
}
}
public enum Levels
{
[Description("Low level")]LOW,
[Description("Normal level")] NORMAL,
[Description("High level")] HIGH
}
вывод результата:
Low level
Normal level
High level