Developer22
Developer22

Reputation: 93

How to sort properties on a class using custom property attributes

I have a custom attribute that I apply to properties on a class. This attribute is used for exporting the class's properties to a flat file.

One of the attribute's properties is FieldOrder. I need to make sure the order in which I export the properties of the class is correct. Also, not all properties on the class will have the custom attribute.

I found this article: How do I sort a generic list based on a custom attribute? This solution assumes all properties have the custom attribute, which isn't my case. I was also hoping for more elegant solution.

Your help is greatly appreciated!

public interface IFileExport{}

public class ExportAttribute: Attribute
{
    public int FieldOrder { get; set; }
    public int FieldLength { get; set; }
    public ExportAttribute() { }
}

public class ExportClass: IFileExport
{
    [ExportAttribute( FieldOrder = 2, FieldLength = 25 )]
    public string LastName { get; set; }

    [ExportAttribute( FieldOrder=1, FieldLength=25)]
    public string FirstName { get; set; }

    [ExportAttribute( FieldOrder = 3, FieldLength = 3 )]
    public int Age { get; set; }

    public ExportClass() { }
}

public class TestClass
{
    public static List<PropertyInfo> GetPropertiesSortedByFieldOrder
                                                            (IFileExport fileExport)
    {
        //get all properties on the IFileExport object
        PropertyInfo[] allProperties = fileExport
                         .GetType()
                         .GetProperties( BindingFlags.Instance | BindingFlags.Public );
        // now I need to figure out which properties have the ExportAttribute 
        //and sort them by the ExportAttribute.FieldOrder
    }
}

UPDATE: I'm ordering the properties by ExportAttribute.FieldOrder Ascending

Upvotes: 9

Views: 15200

Answers (4)

borat
borat

Reputation: 126

You can use either of the following options.

First option: pass anonymous function to OrderBy

return allProperties.OrderBy(m => m.GetCustomAttribute<ExportAttribute>() == null ? -1 : 
                                      m.GetCustomAttribute<ExportAttribute>().FieldOrder).ToList();

Second option: create the function for selecting the key and pass it to OrderBy

return allProperties.OrderBy(KeySelector).ToList();

The key selector function is defined as in here:

    public static int KeySelector(PropertyInfo info)
    {
        ExportAttribute attr = info.GetCustomAttribute<ExportAttribute>();
        return attr == null ? -1 : attr.FieldOrder;
    }

If the property does not have ExportAttribute, the selector will return -1. You can choose any other default value.

The second approach lets you define other types of selectors for ordering and just call the new selector you have defined.

Upvotes: 1

George Mamaladze
George Mamaladze

Reputation: 7921

        static void Main(string[] args)
    {
        //get all properties on the IFileExport object
        PropertyInfo[] allProperties = fileExport.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
        Array.Sort(allProperties, ArrayAttributeComparison);


    }

    private static int ArrayAttributeComparison(PropertyInfo x, PropertyInfo y)
    {
        //Do null checks here
        ExportAttribute xExportAttribute = GetExportAtribute(x);
        ExportAttribute yExportAttribute = GetExportAtribute(x);
        //Do null checks here
        return xExportAttribute.FieldOrder - yExportAttribute.FieldOrder;
    }

    private static ExportAttribute GetExportAtribute(PropertyInfo propertyInfo)
    {
        object[] attributes = propertyInfo.GetCustomAttributes(true);
        foreach (var t in attributes)
        {
            if (t is ExportAttribute)
            {
                return (ExportAttribute)t;
            }
        }
        return null;
    }

Upvotes: 0

Kirk Woll
Kirk Woll

Reputation: 77536

public static List<PropertyInfo> GetPropertiesSortedByFieldOrder( IFileExport    fileExport )
{
    PropertyInfo[] allProperties = GetType()
        .GetProperties(BindingFlags.Instance | BindingFlags.Public)
        .Select(x => new 
        { 
            Property = x, 
            Attribute = (ExportAttribute)Attribute.GetCustomAttribute(x, typeof(ExportAttribute), true) 
        })
        .OrderBy(x => x.Attribute != null ? x.Attribute.FieldOrder : -1)
        .Select(x => x.Property)
        .ToArray();
}

Since you didn't explain how you wanted properties without the attribute ordered, I have made it so that they would be at the beginning. But the gist of this code is:

  1. Get the properties
  2. Throw them into an anonymous type so you have easy access to both the property and the attribute.
  3. Order by the FieldOrder, using -1 for properties without the attribute. (not sure what you wanted here)
  4. Transform the sequence back into a sequence of PropertyInfo
  5. Convert it into a PropertyInfo[] array.

Upvotes: 29

minimalis
minimalis

Reputation: 1953

You should be able to use the GetCustomAttributes() method on each PropertyInfo to filter the properties with the correct attributes, and then sort the remaining items.

Upvotes: 0

Related Questions