views:

92

answers:

4

What I used to do is create a case switch that sorts my LINQ in this format:

List<products> productList = GetAllLists();

switch (sortBy)
{
  case "name":
     return productsList.OrderBy(pl => pl.Name);
  case "date":
     return productsList.OrderBy(pl => pl.DateCreate);
}

which, in the long run, becomes cumbersome.

I wanted to have a generic method that you simply use to sort out List collections like these and will help you toggle which property to sort by.. something like this:

return SortListCollection(productsList, Name);
A: 

Use reflection to build a comparer class that can compare by property name. Then instantiate this class and pass it's compare function to List.Sort

Alex Reitbort
hi alex, can you elaborate more on your idea? thanks!
Martin Ongtangco
A: 

Solved:

private List<T> SortList<T>(List<T> collection, SortOrder order, string propertyName) where T : class
        {
            if (order == SortOrder.Descending)
            {
                return collection.OrderByDescending(cc => cc.GetType().GetProperty(propertyName).GetValue(cc, null)).ToList();
            }

            return collection.OrderBy(cc => cc.GetType().GetProperty(propertyName).GetValue(cc, null)).ToList();
        }
Martin Ongtangco
@Martin: This method will have terrible performance. There is a much faster way. Give me a little time and I will post soon.
Dan Tao
alright Dan, thanks!
Martin Ongtangco
@Martin: just add caching of PropertyInfo (cc.GetType().GetProperty(propertyName)) and you will be fine.
Alex Reitbort
ok alex, i think im settled with the answer. Thanks to all of you who helped!
Martin Ongtangco
+3  A: 

You don't need to use reflection for this, you can do it using expression trees, which will be much faster. Here's a blog post that describes the process, but a cut'n'paste sample is:

public static IQueryable<T> SortBy<T>(this IQueryable<T> source, string propertyName)
{
    var parameter = Expression.Parameter(source.ElementType, String.Empty);
    var property = Expression.Property(parameter, propertyName);
    var lambda = Expression.Lambda(property, parameter);

    var methodCallExpression = Expression.Call(typeof(Queryable), "OrderBy",
        new Type[] { source.ElementType, property.Type },
        source.Expression, Expression.Quote(lambda));

    return source.Provider.CreateQuery<T>(methodCallExpression);
}
Dean Harding
+1  A: 

Do you need to be able to pass in strings? Passing in a Func<T,TResult> delegate will be fast, flexible, and allow runtime checking; passing in a string won't be.

You could even have a bunch of predefined delegates ready-to-go:

var sortedByName = productList.OrderBy(NameSelector);
var sortedByDate = productList.OrderBy(DateCreatedSelector);
var sortedByCustom = productList.OrderBy(p => p.SomeOtherProperty);

// ...

// predefined delegates
public static readonly Func<Product, string> NameSelector = p => p.Name;

public static readonly Func<Product, DateTime> DateCreatedSelector =
                                                     p => p.DateCreated;

And, of course, you could wrap it all up in your own method if you wanted to, but that method would be a superfluous one-liner just wrapping the OrderBy call.

LukeH