简体   繁体   中英

Get properties of class by order using reflection

Please refer to this code

public class A : B
{
     [Display(Name = "Initial Score Code", Order =3)]
     public Code { get; set; }

     [Display(Name = "Initial Score Code", Order =2)]
     public Name{ get; set; }
............

}

I need to get all properties of class through order by orderAttribute of Display. I have tried with this code to do

 var prop = typeof(A)
            .GetProperties()
            .OrderBy(p => ((DisplayAttribute)p.GetCustomAttributes(typeof(DisplayAttribute),  true).FirstOrDefault).Order);

but it causes an error

object reference not to set an instance of object

I assumed this issue because of some property not having "Order" property in "DisplayAttribute" .

How to handle this kind of situation? I need to order all the properties even though some property not having the value of order property.

You are missing brackets () on FirstOrDefault operator. Also you should deal with case when default value is returned. I suggest to select Order value before getting first or default value. That will return 0 for all properties which don't have DisplayAttribute :

var prop = typeof(A)
    .GetProperties()
    .OrderBy(p => p.GetCustomAttributes(typeof(DisplayAttribute), true)
                   .Cast<DisplayAttribute>()
                   .Select(a => a.Order)
                   .FirstOrDefault());

If you want properties without DisplayAttribute to be last, you can provide Int32.MaxValue as default value to be returned:

                   .Select(a => a.Order)
                   .DefaultIfEmpty(Int32.MaxValue)
                   .First()

Try this:

var props = from p in typeof(A).GetProperties()
    let orderAttribute = (DisplayAttribute)(p.GetCustomAttributes(typeof(DisplayAttribute), true))
        .FirstOrDefault()
    where orderAttribute != null
    orderby orderAttribute.Order
    select p;

Or:

var props = from p in typeof(A).GetProperties()
    let orderAttribute = (DisplayAttribute)(p.GetCustomAttributes(typeof(DisplayAttribute), true))
        .FirstOrDefault()
    orderby orderAttribute == null ? 0 : orderAttribute.Order
    select p;

Here is a much more complete answer that allows you to better control the ordering of PropertyInfo instances without DisplayAttribute attributes:

public class A
{
    [Display(Name = "Initial Score Code", Order = 3)]
    public int Code
    {
        get;
        set;
    }

    [Display(Name = "Initial Score Code", Order = 2)]
    public string Name
    {
        get;
        set;
    }
}

public class PropertyInfoComparer : IComparer<PropertyInfo>
{
    public int Compare(PropertyInfo x, PropertyInfo y)
    {
        var attribute1 = (DisplayAttribute)x.GetCustomAttributes(typeof(DisplayAttribute)).FirstOrDefault();
        var attribute2 = (DisplayAttribute)y.GetCustomAttributes(typeof(DisplayAttribute)).FirstOrDefault();

        // If the first property has no attribute, sort it first
        if (attribute1 == null)
        {
            return -1;
        }
        // If the second property has no attribute, sort it first
        if (attribute2 == null)
        {
            return 1;
        }

        // Compare the Order properties of both attributes
        return attribute1.Order.CompareTo(attribute2.Order);
    }
}

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class DisplayAttribute : Attribute
{
    public string Name
    {
        get;
        set;
    }

    public int Order
    {
        get;
        set;
    }
}

Usage:

// Get all the properties of Foo and order them using PropertyInfoComparer
typeof(Foo).GetProperties().OrderBy(arg => arg, new PropertyInfoComparer());

I like the approach with Comparer. However, when I tried it, my iterator went into a dead loop at first. Later, it started throwing exceptions. Also, I optimized it for the case when first property doesn't contain "Order" descriptor to avoid even checking for a second one. I also moved all comments into the class description:

    /// <summary>
    /// If the first property has no attribute, sort it first
    /// If the second property has no attribute, sort it first
    /// Compare the Order properties of both attributes
    /// </summary>
    public class PropertyInfoComparer : IComparer<PropertyInfo>
    {
        public int Compare(PropertyInfo x, PropertyInfo y)
        {
            if (x == y) return 0;

            var attrX = (DisplayAttribute)x.GetCustomAttributes(typeof(DisplayAttribute)).FirstOrDefault();
            int? orderX = attrX?.GetOrder();
            if (orderX == null) return -1;

            var attrY = (DisplayAttribute)y.GetCustomAttributes(typeof(DisplayAttribute)).FirstOrDefault();
            int? orderY = attrY?.GetOrder();
            if (orderY == null) return 1;

            return ((int)orderX).CompareTo((int)orderY);
        }
    }

Unfortunately, classes that have properties without the "Order" descriptor lose their "natural" order. Therefore, I ended up checking for any properties that have the "Order" descriptor first. If at least one of them has that descriptor, then do the sorting.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM