Как сортировать перечисление с использованием настраиваемого атрибута заказа?
У меня есть перечисление вроде этого:
enum MyEnum{
[Order(1)]
ElementA = 1,
[Order(0)]
ElementB = 2,
[Order(2)]
ElementC = 3
}
И я хочу перечислить его элементы, отсортированные по специальному атрибуту заказа, который я написал, чтобы получить список отсортированных элементов.
Я получаю атрибут описания, но только для одного элемента, подобного этому:
FieldInfo fi = value.GetType().GetField(value.ToString());
DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
Это может быть что-то одно и то же, но нужно работать на всех Enum и возвращать список или другое отсортированное перечисление.
Ответы
Ответ 1
Предположим, что класс OrderAttribute
выглядит следующим образом:
public class OrderAttribute : Attribute
{
public readonly int Order;
public OrderAttribute(int order)
{
Order = order;
}
}
Вспомогательный метод для получения отсортированных значений перечисления:
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
Если я четко понял вашу проблему, здесь может быть решение вроде этого:
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();
}
}
использование:
var result = typeof(enumType).EnumGetOrderedValues<enumType>();
Ответ 3
Учитывая
[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;
}
}
}
Вы можете:
int o1 = OrderHelper.GetOrder(MyEnum.ElementA);
int o2 = OrderHelper.GetOrder(MyEnum.ElementB);
int o3 = OrderHelper.GetOrder(MyEnum.ElementC);
Итак, сортировка похожа:
var myenums = new[] { MyEnum.ElementA, MyEnum.ElementB, MyEnum.ElementC };
Array.Sort(myenums, (p, q) => OrderHelper.GetOrder(p).CompareTo(OrderHelper.GetOrder(q)));
или для LINQ:
var myenums = new[] { MyEnum.ElementA, MyEnum.ElementB, MyEnum.ElementC };
var sorted = myenums.OrderBy(x => OrderHelper.GetOrder(x));
OrderHelper
"кэширует" порядок внутри a OrderHelperImpl<TEnum>
. Значения перечисления извлекаются, зная, что в перечислениях значения public static
полей (вы можете легко увидеть это здесь).
Значения без Order
упорядочены в том же порядке, что и в enum
, используя максимально возможные значения int
чуть ниже 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();
}
Это выведет:
значение3
значение1
значение2