How to sort enum using a custom order attribute?

孤街浪徒 提交于 2020-04-29 07:34:26

问题


I have an enum like this:

enum MyEnum{
 [Order(1)]
 ElementA = 1,
 [Order(0)]
 ElementB = 2,
 [Order(2)]
 ElementC = 3
}

And I want to list its elements sorted by a custom order attribute I wrote, so that I get a list of items sorted.

I am getting the Description Attribute but just for one element like this:

FieldInfo fi = value.GetType().GetField(value.ToString());
DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);

It may be something the same but need to work on all Enum and return a list or another sorted enum.


回答1:


Assume the OrderAttribute class is as follows:

public class OrderAttribute : Attribute
{
    public readonly int Order;

    public OrderAttribute(int order)
    {
        Order = order;
    }
}

The helper method to obtain sorted values of enum:

public static T[] SortEnum<T>()
{
    Type myEnumType = typeof(T);
    var enumValues = Enum.GetValues(myEnumType).Cast<T>().ToArray();
    var enumNames = Enum.GetNames(myEnumType);
    int[] enumPositions = Array.ConvertAll(enumNames, n =>
    {
        OrderAttribute orderAttr = (OrderAttribute)myEnumType.GetField(n)
            .GetCustomAttributes(typeof(OrderAttribute), false)[0];
        return orderAttr.Order;
    });

    Array.Sort(enumPositions, enumValues);

    return enumValues;
}



回答2:


If I clearly understood your issue, here could be solution like this:

public static class EnumExtenstions
{
    public static IEnumerable<TEnum> EnumGetOrderedValues<TEnum>(this Type enumType)
    {

        var fields = enumType.GetFields(BindingFlags.Public | BindingFlags.Static);
        var orderedValues = new List<Tuple<int, TEnum>>();
        foreach (var field in fields)
        {
            var orderAtt = field.GetCustomAttributes(typeof(EnumOrderAttribute), false).SingleOrDefault() as EnumOrderAttribute;
            if (orderAtt != null)
            {
                orderedValues.Add(new Tuple<int, TEnum>(orderAtt.Order, (TEnum)field.GetValue(null)));
            }
        }

        return orderedValues.OrderBy(x=>x.Item1).Select(x=>x.Item2).ToList();
    }
}

usage:

var result = typeof(enumType).EnumGetOrderedValues<enumType>();



回答3:


Given

[AttributeUsage(AttributeTargets.Field)]
public class OrderAttribute : Attribute
{
    public readonly int Order;

    public OrderAttribute(int order)
    {
        Order = order;
    }
}

public static class OrderHelper
{
    public static int GetOrder<TEnum>(TEnum value) where TEnum : struct
    {
        int order;

        if (!OrderHelperImpl<TEnum>.Values.TryGetValue(value, out order))
        {
            order = int.MaxValue;
        }

        return order;
    }

    private static class OrderHelperImpl<TEnum>
    {
        public static readonly Dictionary<TEnum, int> Values;

        static OrderHelperImpl()
        {
            var values = new Dictionary<TEnum, int>();

            var fields = typeof(TEnum).GetFields(BindingFlags.Static | BindingFlags.Public);

            int unordered = int.MaxValue - 1;

            for (int i = fields.Length - 1; i >= 0; i--)
            {
                FieldInfo field = fields[i];

                var order = (OrderAttribute)field.GetCustomAttributes(typeof(OrderAttribute), false).FirstOrDefault();

                int order2;

                if (order != null)
                {
                    order2 = order.Order;
                }
                else
                {
                    order2 = unordered;
                    unordered--;
                }

                values[(TEnum)field.GetValue(null)] = order2;
            }

            Values = values;
        }
    }
}

You can then:

int o1 = OrderHelper.GetOrder(MyEnum.ElementA);
int o2 = OrderHelper.GetOrder(MyEnum.ElementB);
int o3 = OrderHelper.GetOrder(MyEnum.ElementC);

So sorting is like:

var myenums = new[] { MyEnum.ElementA, MyEnum.ElementB, MyEnum.ElementC };
Array.Sort(myenums, (p, q) => OrderHelper.GetOrder(p).CompareTo(OrderHelper.GetOrder(q)));

or for LINQ:

var myenums = new[] { MyEnum.ElementA, MyEnum.ElementB, MyEnum.ElementC };
var sorted = myenums.OrderBy(x => OrderHelper.GetOrder(x));

The OrderHelper "caches" the ordering inside a OrderHelperImpl<TEnum>. The values of the enum are extracted by knowing that in enums the values are public static fields (you can easily see it here).

Values without an Order are ordered in the same ordering they are present in the enum, by using the greatest possible values of an int just below int.MaxValue




回答4:


public class OrderAttribute : Attribute
{
    public int priority;

    public OrderAttribute(int priority)
    {
        this.priority = priority;
    }
}

public enum Test
{
    [Order(1)] value1 = 1,
    [Order(2)] value2 = 2,
    [Order(0)] value3 = 3
}

private static void Main(string[] args)
    {
        Dictionary<string, int> priorityTable = new Dictionary<string, int>();

        var values = Enum.GetValues(typeof (Test)).Cast<Test>();
        MemberInfo[] members = typeof (Test).GetMembers();
        foreach (MemberInfo member in members)
        {
            object[] attrs = member.GetCustomAttributes(typeof(OrderAttribute), false);
            foreach (object attr in attrs)
            {
                OrderAttribute orderAttr = attr as OrderAttribute;

                if (orderAttr != null)
                {
                    string propName = member.Name;
                    int priority = orderAttr.priority;

                    priorityTable.Add(propName, priority);
                }
            }
        }

        values = values.OrderBy(n => priorityTable[n.ToString("G")]);

        foreach (var value in values)
        {
            Console.WriteLine(value);
        }

        Console.ReadLine();
    }

This will output:

value3

value1

value2



来源:https://stackoverflow.com/questions/30654151/how-to-sort-enum-using-a-custom-order-attribute

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!