按声明顺序对枚举进行排序

时间:2014-08-05 19:57:07

标签: c# enums

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

有没有办法按顺序对Enum.GetValues(typeof(CurrencyId)).Cast<CurrencyId>()的结果进行排序?它们是在.cs文件(USD,UAH,RUR,EUR,KZT,UNSUPPORTED)中声明的,而不是它们的基础代码?就个人而言,我认为答案是“不”,因为原始订单在二进制文件中丢失了,所以...我该如何实现任务呢?

2 个答案:

答案 0 :(得分:13)

以下是具有自定义属性的版本:

[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public class EnumOrderAttribute : Attribute
{
    public int Order { get; set; }
}


public static class EnumExtenstions
{
    public static IEnumerable<string> GetWithOrder(this Enum enumVal)
    {
        return enumVal.GetType().GetWithOrder();
    }

    public static IEnumerable<string> 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<EnumOrderAttribute>()
                                            })
                                .Select(fieldInfo => new
                                             {
                                                 name = fieldInfo.field.Name,
                                                 order = fieldInfo.attribute != null ? fieldInfo.attribute.Order : 0
                                             })
                               .OrderBy(field => field.order)
                               .Select(field => field.name);
    }
}

用法:

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();

答案 1 :(得分:5)

简答:

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

<强> 原因:

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<T>(string head, IEnumerable<T> 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));
}

<强> 输出:

First time order:
  Enum: Bad, Zero, One
  Fields: first, second
  Properties: First, Second
  Members: get_First, get_Second, ToString, Equals, GetHashCode, GetType, .ctor, Second, First, second, first
Broken order:
  Enum: One, Bad, Zero
  Fields: second, first
  Properties: Second, First
  Members: get_Second, get_First, ToString, Equals, GetHashCode, GetType, .ctor, Second, First, second, first
MetadataToken Sorted:
  Enum: Bad, Zero, One
  Fields: first, second
  Properties: First, Second
  Members: first, second, ToString, Equals, GetHashCode, GetType, get_First, get_Second, .ctor, First, Second

重要说明: MemberInfo.GetFields()由.NET 2.0(读取this nice post about it)后的某些缓存支持,并且可能不会返回声明的字段顺序(更准确地说:编译器发出的顺序似乎保留了一个文件中的顺序,但是未定义合并partial class的顺序)。 Here is similar question on stackoverflow,Marc Gravell的一条评论写道:

  

10.2.6成员[...]类型中成员的排序对C#代码来说很少有意义,但在与C#代码接口时可能很重要   其他语言和环境。在这些情况下,订购   在多个部分中声明的类型中的成员是未定义的。

这样做可以解决缓存问题:

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

MetadataToken排序也可能有所帮助。没有找到保证,但是this应该提供很好的理由为什么它应该有效:

  

低三个字节,称为记录标识符(RID),   包含元数据表中行的索引   令牌的MSB指的是。例如,带有值的元数据标记   0x02000007引用当前作用域中TypeDef表中的第7行。   类似地,令牌0x0400001A引用FieldDef中的第26行(十进制)   当前范围内的表。

原始答案: 使用typeof(CurrencyId).GetFields(),检查FieldInfo.IsStatic(一个__value赢了),然后根据需要使用FieldInfo.NameGetValue

指向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);
    }
}

输出:

USD
UAH
RUR
EUR
KZT
UNSUPPORTED

编辑: The order is not guaranteed :( (见评论)

  

GetFields方法不按特定顺序返回字段,   如字母或声明顺序。您的代码不得依赖   返回字段的顺序,因为该顺序不同。

这可能是.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());
    }
}