SivaRajini
SivaRajini

Reputation: 7375

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.

Upvotes: 4

Views: 4677

Answers (4)

Vladimir Roytman
Vladimir Roytman

Reputation: 41

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.

Upvotes: 0

Sergey Berezovskiy
Sergey Berezovskiy

Reputation: 236228

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

Upvotes: 10

NathanAldenSr
NathanAldenSr

Reputation: 7961

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

Upvotes: 2

Paulo Morgado
Paulo Morgado

Reputation: 14846

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;

Upvotes: 3

Related Questions