如何通过自定义属性对Type属性进行排序

时间:2018-09-20 16:06:46

标签: c# list sorting

我创建了一个自定义属性,该属性实现了多个属性,这些属性将接受其用法值,如下所示。

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 :(得分:1)

这是我的解决方案,有点麻烦,因为如注释中所述,无法保证GetProperties的结果顺序。使用.NET 4.5 +,this answer中描述了一种解决方法。

因此,第一部分是确保无论定制顺序如何,我们都可以按可预定的顺序获取类及其基类的属性。为此,我们从基类向上递归,并收集每个类中声明的属性,然后按属性在类中出现的顺序对结果进行排序。

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

答案 1 :(得分:-3)

您可以使用'GetMembers()'获取'MemberInfo'的数组,然后检查其属性'CustomAttributes'。您可以找到一个示例here