5

I have several Lists<> that holds objects of different classes.

List<classA> listA;
List<classB> listB;
List<classC> listC;
//...
List<classM> listM;

The classes do not relate in any way and have nothing in common except that they might have a single property marked with an Attribute SortBy=true

    [CustomObjectComparerAttribute(IncludeInCompare = true, Title = "View Order", SortBy=true)]
    public int ViewOrder { get; set; }

What I would like to do is to have a generic function that accepts a list of any sort. This is problem Nr 1, because I can not figure out a way to make a function accept Lists<> of different types.

private List<object> SortList(List<object> aList)  //<-- this is not allowed (problem 1)
{
    //Get which property we sort by...
    PropertyInfo  prop = GetPropertyToSortBy(objectA);

    //Sort the list
    //Problem Nr 2: How do I get `PropertyInfo prop` in the statement below
    List<Order> SortedList = aList.OrderBy(o=>o.ThePropToSortBy).ToList();
}
    private PropertyInfo GetPropertyToSortBy(object o)
    {
        Type t = o.GetType();
        foreach (PropertyInfo prop in t.GetProperties(BindingFlags.Instance | BindingFlags.Public))
        {
            if (Attribute.IsDefined(prop, typeof(CustomObjectComparerAttribute)) && ((CustomObjectComparerAttribute)Attribute.GetCustomAttribute(prop, typeof(CustomObjectComparerAttribute))).SortBy == true)
            {
                return prop;
            }
        }
        return null;
    }

I feel that Im attacking this problem from the wrong angle. Am I on the right track?, and if so, how can I solve my problems?

if not, can you point me in the right direction?

  • 6
    Is there a reason you're not just using IComparable? – Ben Aaronson Jun 26 '15 at 17:01
  • No, not really :0). keep in mind I'm quite a rookie. Do you mean to implement IComparable on each class? How would I take advantage od that approach? – David Jun 26 '15 at 17:03
  • I'll write an answer – Ben Aaronson Jun 26 '15 at 17:03
  • 1
    Also, if you implement IComparable<T> on each type, you can use List<T>.Sort(). – Dan Lyons Jun 26 '15 at 18:15
14

Your specific problems are relatively easily solved.

Problem 1 is just a syntax issue for how to write methods with generic type parameters. In your case this would be:

private List<T> SortList<T>(List<T> aList)

It's the <T> after the method name which allows you to use T as a generic type parameter in the rest of the signature

Problem 2 just requires a bit of murky digging into reflection classes. You already have a ProperyInfo instance, so you just need to use PropertyInfo.GetValue, documented here. In other words:

List<Order> SortedList = aList.OrderBy(o=>prop.GetValue(o)).ToList();

You'll also run into some secondary problems- for example, reflection is quite slow, and you're going to be using it many times while sorting. Performance often isn't a primary concern, but for something like sorting a list it's potentially important. I don't know off the top of my head if it'd actually be an issue in this case... but finding out would require worrying about it, research, and all that other stuff you'd rather just avoid altogether.

Also, this is all pretty complex! Now to do something as simple as a list, you're having to deal with attributes, reflection and all that muck.

The route cause of your problems is that you're trying to expose something that should be behavior as metadata. This is a slightly unusual variant of a more common issue that crops up in OO design: exposing something that should be behavior as data.

Fortunately, .NET already gives us a good way to let a class define how it should be sorted in the form of behavior: the IComparable<T> interface. Again, you're probably best off reading the documentation, but in your case, instead of writing:

public class SomeSortableThing
{
    [CustomObjectComparer]
    public int SortByMe {get; set;}
}

You'd write:

public class SomeSortableThing : IComparable<SomeSortableThing>
{
    public int SortByMe {get; set;}
    public int CompareTo(SomeSortableThing other)
    {
        return SortByMe.CompareTo(other.SortByMe);
    }
}

This gives us the advantages:

  • There's no need for reflection or attributes
  • It's idiomatic
  • It's easy to use- any consumer can just use the standard sorting methods without needing any special helper method/class
  • It's much more flexible. If you want to order by one property then another, you can do that easily, rather than having to invent a whole new way to represent that through attributes
  • Our sortable properties don't need to be public, they can be encapsulated if that's appropriate.
  • You can interchangeably sort on properties and fields, without needing to support that in your reflection.
  • As an addendum: if you ever hit a case where it's not appropriate for the class to define how to sort itself, you can use IComparer<T> for that case. It's not relevant here though because the use of attributes is already making it the class's responsibility to define how to sort itself – Ben Aaronson Jun 26 '15 at 17:18
  • Props to you sir. Deleting my answer as yours seems to be more detailed and spot on ;) Upvoted. – Alexus Jun 26 '15 at 17:20
  • I'm pretty sure IComparable isn't something provided by the CLR and is instead just an element of the .NET framework. – whatsisname Jun 26 '15 at 17:34
  • @whatsisname Ack, you're right, I was being sloppy. Fixed. – Ben Aaronson Jun 26 '15 at 18:35
0

.net provides SortedList class in System.Collections.Generic which keeps the list sorted. Your TKey class will need to implement IComparable, or you will need to implement an Comparer for TKey.

If my class was to be sorted in the same way each time, then I would implement IComparable, but if I wanted sometimes to sort on same A then B, but other times say C then A I would write Comparer's and pass then to SortedList.

SortedList is great if you have a large collection that you need to check to see if a key exists in - List can become very slow for these kinds of operations. However, SortedList will be slower to insert values into.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.