Сортировка enum по пользовательскому атрибуту - PullRequest
0 голосов
/ 08 апреля 2020

Я нашел метод расширения для заказа перечисления со значением, хранящимся в пользовательском атрибуте.

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

Я хотел бы изменить это, поэтому я передаю в качестве аргумента атрибут EnumOrderAttribute и выясняю его значение за итерацию. Атрибут EnumOrderAttribute объявлен в другом проекте.

Как я могу изменить этот метод для выполнения sh этого?

1 Ответ

1 голос
/ 08 апреля 2020

Я попытаюсь дать решение относительно моего понимания проблемы.

Насколько я понимаю, OP хочет иметь универсальный метод упорядочения значений c для всех перечисляемых типов, передаваемых в атрибуте. введите и, найдя способ получить значения сравнения, используя этот атрибут.

Вот что я имею в виду:

Давайте предположим, что у нас есть атрибут, с помощью которого мы украшаем наши значения перечисления для упорядочения :

[AttributeUsage(AttributeTargets.Field)]
public class MyEnumOrderAttribute : Attribute
{
    public MyEnumOrderAttribute(int order)
    {
        Order = order;
    }

    public int Order { get; set; }
}

И давайте перечислим наши перечисления, значения которых оформлены с использованием нашего атрибута:

public enum MyEnum
{
    [MyEnumOrder(2)]
    MyVal0 = 0,
    [MyEnumOrder(1)]
    MyVal1,
    [MyEnumOrder(3)]
    MyVal2,
    [MyEnumOrder(0)]
    MyVal3
}

Эти перечисления и атрибуты могут быть нашими или могут быть из сторонней сборки, и мы хотим, чтобы наш метод упорядочил значения с помощью этого атрибута, поэтому нам нужно передать тип атрибута (который уже является универсальным c параметром), и нам нужно передать функцию для получения значения сравнения из экземпляр атрибута.

Следовательно; Вот новая реализация нашего метода расширения: (Некоторые замечания по поводу того, что это метод расширения позже)

public static IEnumerable<TEnum> EnumGetOrderedValues<TEnum, TAttribute>
    (this Type enumType, Func<TAttribute, IComparable> valueExtractor)
    where TAttribute : Attribute
{
    var fields = typeof(TEnum).GetFields(BindingFlags.Public | BindingFlags.Static);
    var orderedValues = new List<Tuple<IComparable, TEnum>>();
    foreach (var field in fields)
    {
        var orderAtt = field.GetCustomAttributes(typeof(TAttribute), false).SingleOrDefault() as TAttribute;
        if (orderAtt != null)
        {
            orderedValues.Add(new Tuple<IComparable, TEnum>(valueExtractor(orderAtt), (TEnum)field.GetValue(null)));
        }
    }

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

Как вы можете видеть, этот метод теперь работает с любым типом атрибута с любым типом значения, используемым для сравнения. (int, string, any IComparable)

А вот как его использовать:

var orderedValues = typeof(MyEnum).EnumGetOrderedValues<MyEnum, MyEnumOrderAttribute>(x => x.Order);

Пожалуйста, не передавайте x => x.Order как функцию, чтобы получить значение атрибута для сравнения. Здесь мы можем передать любую реализацию.

И выходные данные для упорядоченных значений:

    foreach (MyEnum orderedValue in orderedValues)
    {
        Console.WriteLine(orderedValue);
    }

MyVal3
MyVal1
MyVal0
MyVal2

РЕДАКТИРОВАТЬ: Обратите внимание на метод, являющийся методом расширения.

Метод является расширением Type, которое, я думаю, не является необходимым.

В настоящее время мы называем этот метод следующим образом:

typeof(MyEnum).EnumGetOrderedValues<MyEnum, MyEnumOrderAttribute>(x => x.Order);

Метод расширения уже принимает тип enum в своем первом Параметр generi c, и вызов этого метода для любого типа (typeof (string)?) возможен, что может сбить с толку.

Почему бы не использовать его как метод stati c? Если бы это было возможно, будучи расширением, вызывать этот метод для самого перечисления, как MyEnum.EnumGetOrderedValues, это имело бы смысл. Но поскольку это невозможно, мы можем просто изменить его на метод stati c:

public static IEnumerable<TEnum> EnumGetOrderedValues<TEnum, TAttribute>
    (Func<TAttribute, IComparable> valueExtractor)
    where TAttribute : Attribute

и получить тип перечисления внутри, используя typeof(TEnum)

Надеюсь, это поможет

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...