Ordenar enums em ordem de declaração

public enum CurrencyId { USD = 840, UAH = 980, RUR = 643, EUR = 978, KZT = 398, UNSUPPORTED = 0 } 

Existe alguma maneira de classificar os resultados de Enum.GetValues(typeof(CurrencyId)).Cast() por ordem eles são declarados no arquivo .cs (USD, UAH, RUR, EUR, KZT, UNSUPPORTED), não pela sua código subjacente? Pessoalmente, acredito que a resposta seja “não”, porque a ordem original é perdida em binários, então … como posso implementar a tarefa?

Aqui está a versão com o atributo personalizado:

 [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] public class EnumOrderAttribute : Attribute { public int Order { get; set; } } public static class EnumExtenstions { public static IEnumerable GetWithOrder(this Enum enumVal) { return enumVal.GetType().GetWithOrder(); } public static IEnumerable GetWithOrder(this Type type) { if (!type.IsEnum) { throw new ArgumentException("Type must be an enum"); } // caching for result could be useful return type.GetFields() .Where(field => field.IsStatic) .Select(field => new { field, attribute = field.GetCustomAttribute() }) .Select(fieldInfo => new { name = fieldInfo.field.Name, order = fieldInfo.attribute != null ? fieldInfo.attribute.Order : 0 }) .OrderBy(field => field.order) .Select(field => field.name); } } 

Uso:

 public enum TestEnum { [EnumOrder(Order=2)] Second = 1, [EnumOrder(Order=1)] First = 4, [EnumOrder(Order=3)] Third = 0 } var names = typeof(TestEnum).GetWithOrder(); var names = TestEnum.First.GetWithOrder(); 

Resposta curta:

 foreach(FieldInfo fi in typeof(CurrencyId).GetFields() .Where(fi => fi.IsStatic).OrderBy(fi => fi.MetadataToken)) Console.WriteLine(fi.Name); 

Razão:

 public enum EnumOrder { Bad = -1, Zero = 0, One = 1 } public class ClassOrder { public int first; public int First { get { return first; } } public int second; public int Second { get { return second; } } } private void PrintInfos(string head, IEnumerable list) where T: MemberInfo { memo.AppendText(string.Format(" {0}: ", head)); bool first = true; foreach(var e in list) { if(first) first = false; else memo.AppendText(", "); memo.AppendText(e.Name); } memo.AppendText("\r\n"); } private void ReflectionOrderTest(object sender, EventArgs e) { typeof(EnumOrder).GetField("One"); typeof(ClassOrder).GetField("second"); typeof(ClassOrder).GetProperty("Second"); memo.AppendLine("First time order:"); PrintInfos("Enum", typeof(EnumOrder).GetFields().Where(fi => fi.IsStatic)); PrintInfos("Fields", typeof(ClassOrder).GetFields()); PrintInfos("Properties", typeof(ClassOrder).GetProperties()); PrintInfos("Members", typeof(ClassOrder).GetMembers()); memo.AppendLine("Broken order:"); PrintInfos("Enum", typeof(EnumOrder).GetFields().Where(fi => fi.IsStatic)); PrintInfos("Fields", typeof(ClassOrder).GetFields()); PrintInfos("Properties", typeof(ClassOrder).GetProperties()); PrintInfos("Members", typeof(ClassOrder).GetMembers()); memo.AppendLine("MetadataToken Sorted:"); PrintInfos("Enum", typeof(EnumOrder).GetFields().Where(fi => fi.IsStatic).OrderBy(fi => fi.MetadataToken)); PrintInfos("Fields", typeof(ClassOrder).GetFields().OrderBy(fi => fi.MetadataToken)); PrintInfos("Properties", typeof(ClassOrder).GetProperties().OrderBy(fi => fi.MetadataToken)); PrintInfos("Members", typeof(ClassOrder).GetMembers().OrderBy(fi => fi.MetadataToken)); } 

Saída:

 Primeira ordem do tempo:
   Enum: Bad, Zero, One
   Campos: primeiro, segundo
   Propriedades: primeiro, segundo
   Membros: get_First, get_Second, ToString, Equals, GetHashCode, GetType, .ctor, segundo, primeiro, segundo, primeiro
 Ordem quebrada:
   Enum: um, ruim, zero
   Campos: segundo, primeiro
   Propriedades: segundo, primeiro
   Membros: get_Second, get_First, ToString, Equals, GetHashCode, GetType, .ctor, segundo, primeiro, segundo, primeiro
 MetadataToken Sorted:
   Enum: Bad, Zero, One
   Campos: primeiro, segundo
   Propriedades: primeiro, segundo
   Membros: primeiro, segundo, ToString, Equals, GetHashCode, GetType, get_First, get_Second, .ctor, primeiro, segundo

NOTAS IMPORTANTES: MemberInfo.GetFields() é apoiado por algum cache desde o .NET 2.0 (leia este post interessante sobre ele ) e pode não retornar os campos em ordem declarada (mais precisamente: a ordem emitida pelo compilador que parece preservar a ordem em um arquivo, mas a ordem da partial class mesclada é indefinida). Aqui está uma pergunta semelhante em stackoverflow , e um comentário de Marc Gravell diz:

10.2.6 Membros […] A ordenação de membros dentro de um tipo raramente é significativa para o código C #, mas pode ser significativa ao fazer interface com outros idiomas e ambientes. Nestes casos, a ordenação de membros dentro de um tipo declarado em múltiplas partes é indefinida.

Isso deve superar o problema com o cache:

 GC.Collect(); GC.WaitForPendingFinalizers(); var fields = typeof(Whatever).GetFields(); 

A sorting por MetadataToken também pode ajudar. Não encontrou uma garantia, mas isso deve fornecer um bom raciocínio por que deveria funcionar:

Os três bytes inferiores, referidos como o identificador de registro (RID), contêm o índice da linha na tabela de metadados à qual o MSB do token se refere. Por exemplo, o token de metadados com valor 0x02000007 se refere à linha 7 na tabela TypeDef no escopo atual. Da mesma forma, o token 0x0400001A se refere à linha 26 (decimal) na tabela FieldDef no escopo atual.

RESPOSTA ORIGINAL: Use typeof(CurrencyId).GetFields() , verifique FieldInfo.IsStatic (um __value não será) e use FieldInfo.Name ou GetValue conforme necessário.

Link para IDEONE: http://ideone.com/hnT6YL

 using System; using System.Reflection; public class Test { public enum CurrencyId { USD = 840, UAH = 980, RUR = 643, EUR = 978, KZT = 398, UNSUPPORTED = 0 } public static void Main() { foreach(FieldInfo fi in typeof(CurrencyId).GetFields()) if(fi.IsStatic) Console.WriteLine(fi.Name); } } 

Saída:

 USD UAH RUR EUR KZT UNSUPPORTED 

EDIT: A ordem não é garantida 🙁 (Ver comentários)

O método GetFields não retorna campos em uma ordem específica, como ordem alfabética ou de declaração. Seu código não deve depender da ordem em que os campos são retornados, porque esse pedido varia.

Isso pode ser uma solução para o .NET 4.5

 using System; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; [AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = false)] public sealed class OrderAttribute : Attribute { private readonly int order_; public OrderAttribute( [CallerLineNumber] int order = 0) { order_ = order; } public int Order { get { return order_; } } } public class Test { public enum CurrencyId { [Order] USD = 840, [Order] UAH = 980, [Order] RUR = 643, [Order] EUR = 978, [Order] KZT = 398, [Order] UNSUPPORTED = 0 } public static void Main() { foreach(FieldInfo fi in typeof(CurrencyId).GetFields() .Where(fi => fi.IsStatic) .OrderBy(fi => ((OrderAttribute)fi.GetCustomAttributes( typeof(OrderAttribute), true)[0]).Order)) Console.WriteLine(fi.GetValue(null).ToString()); } }