In this post we are gonna re-invent the LINQ's OrderBy extension method.
The implementation of OrderBy allows us to pass lambda expression in the form: x => x.Name
so to order list of students by name, you do the following:
var orderedStudents = allStudents.OrderBy(s => s.Name);
but what if you want to order dynamically by the property name ("Name") in run-time (from data storage source for example)
what would you do, do you use switch-case?
Nah, there's a much more better solution than this and it's 3~5 lines of code!
Lambda Expressions
We need the lambda expression to be replaced by a string parameter, fortunately it's easy building up a lambda expression by the property name and the object type.
Let's break down the lambda expression passed to OrderBy : x => x.Name
x is the parameter and it's of type T which get inferred in compile time from the invoked list.
Name is the member
so let's create them:
Parameter:
var param = Expression.Parameter(typeof(Foo), "p");
Member:
var member = Expression.Property(param, "Name");
then assemble the lambda expression:
var lambda = Expression.Lambda<Func<Foo, object>>(member, param);
lambda will only work on IQueryable because it's of type Expression<Func<Foo, object>>
to work on IEnumerable we need to compile it:
lambda.Compile();
the following is the complete code:
The implementation of OrderBy allows us to pass lambda expression in the form: x => x.Name
so to order list of students by name, you do the following:
var orderedStudents = allStudents.OrderBy(s => s.Name);
but what if you want to order dynamically by the property name ("Name") in run-time (from data storage source for example)
what would you do, do you use switch-case?
Nah, there's a much more better solution than this and it's 3~5 lines of code!
Lambda Expressions
We need the lambda expression to be replaced by a string parameter, fortunately it's easy building up a lambda expression by the property name and the object type.
Let's break down the lambda expression passed to OrderBy : x => x.Name
x is the parameter and it's of type T which get inferred in compile time from the invoked list.
Name is the member
so let's create them:
Parameter:
var param = Expression.Parameter(typeof(Foo), "p");
Member:
var member = Expression.Property(param, "Name");
then assemble the lambda expression:
var lambda = Expression.Lambda<Func<Foo, object>>(member, param);
lambda will only work on IQueryable because it's of type Expression<Func<Foo, object>>
to work on IEnumerable we need to compile it:
lambda.Compile();
the following is the complete code:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static class NiceLinq | |
{ | |
public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> notOrderedList, string propertyName) | |
{ | |
var lambda = GetLambda<T>(propertyName); | |
var orderedList = notOrderedList.OrderBy(lambda); | |
return orderedList; | |
} | |
private static Func<T, object> GetLambda<T>(string propertyName) | |
{ | |
var param = Expression.Parameter(typeof(T), "p"); | |
var member = Expression.Property(param, propertyName); | |
Expression conversion = Expression.Convert(member, typeof(object)); //explicit casting is a must | |
var lambda = Expression.Lambda<Func<T, object>>(conversion, param); | |
return lambda.Compile(); | |
} | |
} | |
public class Program | |
{ | |
public static void Main(String[] args) | |
{ | |
//some code from data storage source | |
var orderedStudents = allStudents.OrderBy("Name"); | |
} | |
} |