Obtener una lista de mis atributos de enumeración con un método genérico

Al inicio, tenemos esta enumeración básica.

public enum E_Levels { [ValueOfEnum("Low level")] LOW, [ValueOfEnum("Normal level")] NORMAL, [ValueOfEnum("High level")] HIGH } 

Y me gustaría obtener una List cualquiera que sea la enumeración . Algo como Extensions.GetValuesOfEnum() que podría devolver una List con “Nivel bajo”, “Nivel normal” y “Nivel alto”.

StackOF me ayudó a obtener un atributo de valor:

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

Y puedo llamar a este método cualquiera que sea la enumeración: E_Levels.LOW.ToValueOfEnum() .

Además, StackOF me ayudó a obtener una List para una enumeración específica . Hice este método en un controlador:

 private List GetLevels() { List levelsToReturn = new List(); var levels = Enum.GetValues(typeof(E_Levels)).Cast(); foreach(E_Levels l in levels) levelsToReturn.Add(l.ToValueOfEnum()); return levelsToReturn; } 

Pero de esta manera me obliga a volver a escribir el mismo método para cada enumeración.
Así que intenté agregar este método genérico a mis Extensiones de clase:

 public static class Extensions { public static string ToValueOfEnum(this Enum value) {...} public static List GetValuesOf() { List levelsToReturn = new List(); var levels = Enum.GetValues(typeof(T)).Cast(); foreach(T l in levels) levelsToReturn.Add(l.ToValueOfEnum()); return levelsToReturn; } } 

Pero en mi foreach, .ToValueOfEnum() es un método desconocido.

Así que estoy en problemas, esperaba poder encontrar una manera de no volver a escribir una y otra vez el mismo método para cada enumeración …

Tratemos de mantener este propósito más general.

Tengo un método de extensión que podría tomar atributos de los valores de enumeración. Esto le daría un acceso rápido a los atributos.

 public static class EnumExtensions { public static TAttribute GetAttribute(this Enum value) where TAttribute : Attribute { var type = value.GetType(); var name = Enum.GetName(type, value); return type.GetField(name) .GetCustomAttributes(false) .OfType() .SingleOrDefault(); } } 

Usando esto, puedes crear algunas consultas para obtener lo que deseas.

 var valuesOfLevels = Enum.GetValues(typeof(E_Levels)).Cast() .Select(level => level.GetAttribute().Value); 

Por lo tanto, su método GetValuesOf() (que no es un gran nombre para tal método de especialización IMHO) se puede escribir así:

 public static List GetValuesOf() where TEnum : struct // can't constrain to enums so closest thing { return Enum.GetValues(typeof(TEnum)).Cast() .Select(val => val.GetAttribute().Value) .ToList(); } 

Ahora puedes llamar al método así:

 var levelValues = GetValueOf(); // levelValues = { "Low level", "Normal level", "High level" } 

Puede intentar convertir (Enum)(object)l , cambiando ToValueOfEnum para tomar un object , o simplemente alinear el método:

 public static List GetValuesOf() { List levelsToReturn = new List(); var levels = Enum.GetValues(typeof(T)).Cast(); 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; } 

Aquí hay una solución de una línea que utiliza el enfoque de casting:

 return new List(Enum.GetValues(typeof(T)).Cast().Select(x => x.ToValueOfEnum())); 

En caso de que no tengas claro por qué T no fue reconocido como un Enum como E_Levels , es porque no especificaste ese T : enum . Desafortunadamente, no puede especificar eso en C # (aunque el CLR lo admite), por lo que otros enfoques, como la comprobación / suposición de tiempo de ejecución (como lo que sugiero aquí) o las modificaciones de código posteriores a la comstackción (por ejemplo , melodía sin restricciones ) tienen para acabar.

.Net ya tiene el mismo atributo Description por lo que puede usar este en lugar de ValueOfEnum .

¿Qué pasa con la dynamic y la extensión en el type y siguiente ejemplo

 [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 ToValues(this Type value) { var result = new List(); var values = ToConcreteValues(value); foreach (dynamic item in values) { Description attribute = GetAttribute(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(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 } 

resultado de salida:

 Low level Normal level High level