Как отсортировать свойства типа по пользовательскому атрибуту - PullRequest
0 голосов
/ 20 сентября 2018

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

public class CustomAttribute : OrderAttribute
{
    public string Name { get; set; }

    public bool Ignore { get; set; }

    public override int Order { get; set; } = -1;
}

public abstract OrderAttribute : Attribute
{
    public virtual int Order { get; set; }
}

После этого я создал модели классов для использования CustomAttribute.

public abstract class Person
{
    [Custom(Name = "Frist Name")]
    public string FirstName { get; set; }

    [Custom(Name = "Last Name")]
    public string LastName { get; set; }

    [Custom(Name = "Email")]
    public string Email { get; set; }
}

public class Student : Person
{
    [Custom(Name = "Address", Order = 2)]
    public string Address { get; set; }

    [Custom(Name = "Grade", Order = 5)]
    public int Grade { get; set; }
}

Поскольку я использую это с отражением C #, я должен использовать typeof и получить свойства класса.Это когда мне нужно отсортировать порядок свойств на основе их определенного порядка, используя OrderBy.

typeof(Student).GetProperties(BindingFlags.Public | BindingFlags.Instance)
    .OrderBy(KeySelector)
    .ToList();

Используя этот метод в OrderBy.

private static int KeySelector(PropertyInfo prop)
{
    var attr = prop.GetCustomAttribute<CustomAttribute>();
    return attr?.Order ?? -1;
}

Тем не менее, он делает следующее:

  1. Имя
  2. Фамилия
  3. Электронная почта
  4. Адрес
  5. Класс

Я хочу, чтобы любое свойство, которое имеет [Custom(Name = "Test")] и не реализует свойство Order, должно быть сохранено в его заказе или имеет значение заказа по умолчанию -1.Таким образом, порядок должен быть примерно таким:

  1. Имя
  2. Адрес
  3. Фамилия
  4. Электронная почта
  5. Оценка

Ответы [ 2 ]

0 голосов
/ 20 сентября 2018

Вот мое решение, оно немного затянуто, потому что, как указано в комментариях, порядок результата GetProperties не гарантируется.В .NET 4.5+ есть способ обойти это, как описано в этом ответе .

Итак, первая часть - убедиться, что мы можем получить свойства класса и его базового класса.(е) в предсказуемом порядке, независимо от нашего пользовательского заказа.Для этого мы возвращаемся из базового класса вверх и собираем свойства, объявленные в каждом, сортируя результаты по порядку, в котором свойство появляется в классе.

public static IEnumerable<PropertyMetaData> GetPropertiesOrdered(Type someType, int inheritanceLevel = 1)
{
    List<PropertyMetaData> seenProperties = new List<PropertyMetaData>();
    if (someType.BaseType != (typeof(object)))
        seenProperties.AddRange(GetPropertiesOrdered(someType.BaseType, inheritanceLevel + 1));

    var properties = someType
        .GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
        .Select(a => new PropertyMetaData(a, inheritanceLevel))
        .Where(a => a.AttributeData != null);

    properties = properties
        .OrderBy(a => a.AttributeData.ClassOrder)
        .Select((a, ordinal) =>
        {
            a.OrderWithinClass = ordinal + 1;
            return a;
        });                        

    return seenProperties
        .Union(properties)
        .OrderByDescending(a => a.InheritanceLevel)
        .ThenBy(a => a.OrderWithinClass)
        .Select((a, ordinal) =>
        {
            a.OrderOverall = ordinal + 1;
            return a;
        });
}

Для поддержки этого атрибутизменяется немного, как показано ниже, для использования атрибута номера строки компилятора, как в связанном ответе:

public class CustomAttribute : OrderAttribute
{
    public CustomAttribute([CallerLineNumber]int order = 0) : base(order)
    {

    }

    public string Name { get; set; }
    public bool Ignore { get; set; }
    public override int Order { get; set; } = -1;
}

public abstract class OrderAttribute : Attribute
{
    private readonly int _classOrder;
    public OrderAttribute([CallerLineNumber]int order = 0)
    {
        _classOrder = order;
    }

    public int ClassOrder { get { return _classOrder;  } }
    public virtual int Order { get; set; }
}

Итак, теперь мы получили существующие свойства в предсказуемом, пронумерованном порядке, начиная с базового класса Person свойства затем следуют верхний уровень Student свойства класса.Выходные данные метода GetPropertiesOrdered представляют собой контейнерный класс с информацией о порядке встретившихся свойств, настраиваемом атрибуте, связанном с каждым, и коде для обработки настраиваемой сортировки - то есть, если порядок определен, то предпочтительнее егопорядок свойств, видимых в типе.

public class PropertyMetaData : IComparable<PropertyMetaData>
{
    public PropertyMetaData(PropertyInfo propertyInfo, int inheritanceLevel)
    {
        InheritanceLevel = inheritanceLevel;
        PropertyInfo = propertyInfo;
        AttributeData = propertyInfo.GetCustomAttribute<CustomAttribute>();
    }

    public int InheritanceLevel { get; set; }
    public int OrderWithinClass { get; set; }
    public int OrderOverall { get; set; }

    public CustomAttribute AttributeData { get; set; }            
    public PropertyInfo PropertyInfo { get; set; }

    public int GetOrder()
    {
        return HasCustomOrder() ? AttributeData.Order : this.OrderOverall;
    }

    public bool HasCustomOrder()
    {
        return AttributeData.Order != -1;
    }

    public int CompareTo(PropertyMetaData other)
    {
        var myOrder = GetOrder();
        var otherOrder = other.GetOrder();
        int compare = myOrder.CompareTo(otherOrder);
        if (compare != 0 || other == this) return compare;
        if (HasCustomOrder() && other.HasCustomOrder()) return 0;
        if (HasCustomOrder() && !other.HasCustomOrder()) return -1;
        return 1;
    }
}

В целом, его можно назвать так:

var propertiesSorted =
    GetPropertiesOrdered(typeof(Student))
    .OrderBy(a => a);

, который возвращает нам поля в требуемом порядке для вашего примера.: https://dotnetfiddle.net/dd5hVN

0 голосов
/ 20 сентября 2018

Вы можете использовать 'GetMembers ()', чтобы получить массив 'MemberInfo', а затем проверить их свойство 'CustomAttributes'.Пример вы можете найти здесь

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