1

Often when offering a user specified sort order, and using LINQ for sorting, I end up with an ugly scenario like this:

public static IEnumerable<ConfirmationItemViewModel> SortAscending(IEnumerable<ConfirmationItemViewModel> confirmations, string sortProperty)
{
    switch (sortProperty)
    {
        case "CreatedOn":
            confirmations = confirmations.OrderBy(i => i.CreatedOn).ToList();
            break;
        case "PaymentId":
            confirmations = confirmations.OrderBy(i => i.PaymentId).ToList();
            break;
        default:
            confirmations = confirmations.OrderBy(i => i.PaymentId).ThenBy(i => i.CreatedOn).ToList();
            break;
    }
    return confirmations;
}

The OrderBy method takes a function delegate of the type Func<TSource, TKey>, which I presume it uses to get the value of the sort property from each item in the collection being sorted. I would like to write a method that takes the property name instead of a delegate, and returns a delegate that returns the property value, if that even half explains what I mean.

Hopefully my attempt at coding it, which doesn't work, will explain more. This is the closest I have been able to get, given my limited understanding of expressions and delegates:

public static Func<TObject, TKey> BuildKeySelector<TObject, TKey>(TObject source, string propertyName)
{
    return obj =>
    {
        var prop = source.GetType().GetProperty(propertyName, typeof(TKey));
        return (TKey) prop.GetValue(obj);
    };
}

static void Main(string[] args)
{
    // Sort a list of Person objects by their Name property.
    var peeps = new List<Person>();
    var rank = peeps.OrderBy(BuildKeySelector(<something>, "Name"));
}
Peanuts
  • 13
  • 3

1 Answers1

2

You don't need TObject object as a parameter. This becomes clear if you see that you only use source to get the type.

Here is how you can do it:

public static Func<TObject, TKey> BuildKeySelector<TObject, TKey>(string propertyName)
{
    return obj =>
    {
        var prop = typeof(TObject).GetProperty(propertyName, typeof(TKey));
        return (TKey) prop.GetValue(obj);
    };
}

However, this is not very efficient since your function (the delegate the you return from the BuildKeySelector method) would use reflection every time to get the property value. A better approach builds an expression (that you can cache) and compiles the expression into a delegate.

Yacoub Massad
  • 26,006
  • 2
  • 31
  • 56
  • Quite correct, thanks. The only inconvenience is having to specify object and property types, e.g. `var rank = peeps.OrderBy(BuildKeySelector("Name"));`, but I suppose the poor compiler can't infer the type of a property that is only determined inside the method. – Peanuts Jan 17 '16 at 15:55