Click here to Skip to main content
15,860,972 members
Articles / Programming Languages / C#

Expression Tree Basics

Rate me:
Please Sign up or sign in to vote.
4.74/5 (48 votes)
20 Sep 2012CPOL8 min read 145.4K   1.7K   84   27
Using C# Expression Trees to build a custom sort routine

Introduction

I expect the reader to be familiar with C# Generics and Reflection.

Some days ago, in the project I am currently working on, I encountered an interesting problem. The problem was to sort a list of objects based on some property.

Let's say we have a Person class:

C#
public class Person
{
	public string Name { get; set; }
	public int Age { get; set; }
	public DateTime JoiningDate { get; set; }
}

And a list of Person objects:

C#
Person p1 = new Person { Name = "Kayes", Age = 29, JoiningDate = DateTime.Parse("2010-06-06") };
Person p2 = new Person { Name = "Gibbs", Age = 34, JoiningDate = DateTime.Parse("2008-04-23") };
Person p3 = new Person { Name = "Steyn", Age = 28, JoiningDate = DateTime.Parse("2011-02-17") };

List<Person> persons = new List<Person>();
persons.Add(p1);
persons.Add(p2);
persons.Add(p3);

Suppose we need to sort the persons list by Age in ascending order. Now, any C# 3.0 rookie (like me) would do this in the following way:

C#
List<Person> sortedList = persons.OrderBy<Person, int>(p => p.Age).ToList();

The OrderBy() would sort the list in ascending order by the Age property and the persons list would look like this:

ExpressionTreeBasics/ExpressionTreeBasics01.jpg

So where is the problem? Everything is working fine here! Right. The problem arose when I realized that the property I need to sort the list by is determined at runtime. How so? I had my persons list projected on an HTML gridview where each property of the Person object is shown in each column of the grid. Now, my client wants to click on the column header to sort the grid data by that column, i.e., the corresponding property of the Person object. Say, the client clicks on the Age column. So the grid data (the persons) would be sorted by the Age property.

When the user clicks on the column, I somehow get the name of the property as a string in the code responsible for sorting the grid data. How the name of the property gets sent to the backend code is beyond the scope of this article. So, we will just assume that the property name is available to us and we just need to sort the list of persons by that property.

Background

When we wrote the OrderBy() call above to sort the list, we told the method the type of the property to sort the list by, as a generic type parameter and the name of the property using a lambda expression. Therefore the property and its type are determined at compile time. But we cannot afford to do that because there are 3 different properties of 3 different data types in our Person object. The user may want to sort the grid by any property. So, we somehow have to determine the type and name of the property at runtime. We assumed that we have the name of the property at our disposal. So, identifying the type and using this information to sort the list are what remain to be done.

And along comes Expression Tree to our rescue. Expression Tree is a language feature introduced in C# 3.0. It is used to build lambda expressions dynamically at runtime. More on Expression Trees here.

Let's Cut the Crap and Get Straight Down into the Code

Right. That's what we coders live for. Code. ;)

We need to build the lambda expression that we used above to call the OrderBy() method. Which is

C#
p => p.Age

p, here, is the Person object that is passed in the lambda expression. So, first we will create the ParameterExpression. A ParameterExpression denotes the input variable to the left hand side of the lambda (=>) operator in the lambda expression.

C#
ParameterExpression pe = Expression.Parameter(typeof(Person), "p");

We are giving the name of the parameter exactly as we did when we hard-coded the lambda expression above. Now to build the body of the lambda expression. And here's how we do it:

C#
Expression<Func<Person, int>> expr = 
	Expression.Lambda<Func<Person, int>>(Expression.Property(pe, "Age"), pe);

Let's analyze the above line. First of all, what is this Func<Person, int> that is passed as the generic type parameter to the Expression type?

The answer: Func<Person, int> is a delegate that encapsulates a method that has one parameter (Person) and returns a value of the type specified by the second parameter (int). The signature is:

C#
public delegate TResult Func<in T, out TResult>( T arg )

Basically the whole point is the expression we are to build reflects a method denoted by Func<Person, int> which is what we passed as a hard-wired lambda expression at the beginning of this article. And now we're just building the same with expression tree.

But how do we know what the delegate looks like? That is to say, how can we tell how many input parameters the delegate would take or whether the delegate would return a value at all? Easy. The thing is we are building an expression that we eventually will use to call the OrderBy() extension method on a IEnumerable<T> object, i.e, our persons list. The signature of OrderBy() is as follows:

C#
public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(
	this IEnumerable<TSource> source,
	Func<TSource, TKey> keySelector
)

So that's how we know what the keySelector delegate will look like. TSource is the type of the object, the list of which we are to sort. TKey is the type of the property by which we want to sort the list. Let's resume the rest of the analysis.

C#
Expression<Func<Person, int>> expr = 
	Expression.Lambda<Func<Person, int>>(Expression.Property(pe, "Age"), pe);

The first parameter that the Expression.Lambda() static method takes is the body expression. In our desired lambda expression, the body is just all about returning a property of the Person object that we have access to through the parameter.

We will create the body expression using the Expression.Property() static method which represents accessing a property in an object. We pass the ParameterExpression pe that we created earlier, as the first parameter to the Expression.Property() method and the name of the property as the second parameter.

And then again the ParameterExpression pe as the second parameter to Expression.Lambda() method which is to tell what our input variable to the lambda expression is.

Here's an important issue to note. In the above code snippet, we are creating the expression with explicit types in the generic type declaration for the delegate. We are hard coding the type of the object (Person), the list of which we are going to sort. We are also hard coding the type of the property (int) by which the list would be sorted. The list type might have been known in most cases but we can't afford to hard code the return type of the delegate as we would not know by which type of property the list would be sorted. But later in the article, we are going to wrap our code in a generic method. Therefore we will have the list type and the return type at our disposal through generic type parameters. But for the sake of simplicity for the discussion, let's create the expression with implicit type var.

C#
var expr = Expression.Lambda(Expression.Property(pe, "Age"), pe);

At this point, our expression is done and if we run a quick watch in Visual Studio on expr, it would look like this:

ExpressionTreeBasics/ExpressionTreeBasics02.jpg

Exactly the same as what we have hard-coded at the beginning. Now let's put together all the codes we have written so far:  

C#
Person p1 = new Person { Name = "Kayes", Age = 29, JoiningDate = DateTime.Parse("2010-06-06") };
Person p2 = new Person { Name = "Gibbs", Age = 34, JoiningDate = DateTime.Parse("2008-04-23") };
Person p3 = new Person { Name = "Steyn", Age = 28, JoiningDate = DateTime.Parse("2011-02-17") };

List<Person> persons = new List<Person>();
persons.Add(p1);
persons.Add(p2);
persons.Add(p3);

string sortByProp = "Age";
ParameterExpression pe = Expression.Parameter(typeof(Person), "p");
var expr = Expression.Lambda(Expression.Property(pe, sortByProp), pe);

Note that we kept the name of the property in a variable and used that variable in building the expression.

All we have to do now is use the expression in the OrderBy() method call.

C#
List<Person> sortedList = persons.OrderBy<Person, int>(expr).ToList();

But wait.. what's this?

ExpressionTreeBasics/ExpressionTreeBasics03.jpg

Seems like there's an error:

'System.Collections.Generic.List<CustomSort.Person>' does not contain a 
definition for 'OrderBy' and the best extension method overload 
'System.Linq.Queryable.OrderBy<TSource,TKey>(System.Linq.IQueryable<TSource>, 
System.Linq.Expressions.Expression<System.Func<TSource,TKey>>)' 
has some invalid arguments. 

That is because our persons list is a type of IEnumerable<t /> which does not have an OrderBy() overload which takes an expression. What do we do now? We just need to convert our list to a IQueryable<T>

C#
IQueryable<Person> query = persons.AsQueryable();

and call OrderBy() on the IQueryable<Person>. 

C#
List<Person> sortedList = query.OrderBy<Person, int>(expr).ToList();

Therefore, our revised code would look like the following:

C#
Person p1 = new Person { Name = "Kayes", Age = 29, JoiningDate = DateTime.Parse("2010-06-06") };
Person p2 = new Person { Name = "Gibbs", Age = 34, JoiningDate = DateTime.Parse("2008-04-23") };
Person p3 = new Person { Name = "Steyn", Age = 28, JoiningDate = DateTime.Parse("2011-02-17") };

List<Person> persons = new List<Person>();
persons.Add(p1);
persons.Add(p2);
persons.Add(p3);

string sortByProp = "Age";
ParameterExpression pe = Expression.Parameter(typeof(Person), "p");
var expr = Expression.Lambda(Expression.Property(pe, sortByProp), pe);

IQueryable<Person> query = persons.AsQueryable();
List<Person> sortedList = query.OrderBy<Person, int>(expr).ToList();

So we have our expression working with OrderBy(). But are we done yet? Not quite. Why? Because we are still hard-coding the type of the property that we want to sort our list by, as the generic type parameter to the OrderBy() method. We need to somehow determine this type at runtime. And the way to do this is by using reflection in the following way:

C#
Type sortByPropType = typeof(Person).GetProperty(sortByProp).PropertyType;

Now we need to call OrderBy() by passing the type stored in sortByPropType variable. And here, also, reflection comes into play. First we need to get the corresponding MethodInfo object for the static generic OrderBy() method which is an extension method defined in the static Queryable class.

C#
MethodInfo orderByMethodInfo = typeof(Queryable)
			.GetMethods(BindingFlags.Public | BindingFlags.Static)
			.Single(mi => mi.Name == "OrderBy"
			&& mi.IsGenericMethodDefinition
			&& mi.GetGenericArguments().Length == 2
			&& mi.GetParameters().Length == 2
		);

Now that we have our MethodInfo object for the OrderBy() method, we need to invoke it in the following way:

C#
List<Person> sortedList = (orderByMethodInfo.MakeGenericMethod(new Type[] 
	{ typeof(Person), sortByPropType }).Invoke(query, new object[] 
	{ query, expr }) as IOrderedQueryable<Person>).ToList();

The above reflection codes are definitely not easy on the eyes, I know ;)

But that's it. We have our list sorted by Age in ascending order in the sortedList variable.

Final Code

Let's see what our final code looks like: 

C#
class Program
{
	static void Main(string[] args)
	{
		Person p1 = new Person { Name = "Kayes", Age = 29, JoiningDate = DateTime.Parse("2010-06-06") };
		Person p2 = new Person { Name = "Gibbs", Age = 34, JoiningDate = DateTime.Parse("2008-04-23") };
		Person p3 = new Person { Name = "Steyn", Age = 28, JoiningDate = DateTime.Parse("2011-02-17") };

		List<Person> persons = new List<Person>();
		persons.Add(p1);
		persons.Add(p2);
		persons.Add(p3);

		string sortByProp = "Age";
		Type sortByPropType = typeof(Person).GetProperty(sortByProp).PropertyType;
		
		ParameterExpression pe = Expression.Parameter(typeof(Person), "p");
		var expr = Expression.Lambda(Expression.Property(pe, sortByProp), pe);

		IQueryable<Person> query = persons.AsQueryable();
		
		MethodInfo orderByMethodInfo = 
		typeof(Queryable).GetMethods(BindingFlags.Public | BindingFlags.Static)
				.Single(mi => mi.Name == "OrderBy"
				&& mi.IsGenericMethodDefinition
				&& mi.GetGenericArguments().Length == 2
				&& mi.GetParameters().Length == 2
		);

		List<Person> sortedList = 
			(orderByMethodInfo.MakeGenericMethod(new Type[] 
			{ typeof(Person), sortByPropType }).Invoke(query, 
			new object[] { query, expr }) as 
			IOrderedQueryable<Person>).ToList();
	}
}

Improvements

While our code above works just fine, it is heavily dependent on the Person object. So if need be, for lists of other types of objects we have to write basically the same code with different objects other than Person. So I was thinking of making the code reusable and generic so that it can accommodate any type of object we provide. We can do it by creating an extension method for the IEnumerable type.

C#
public static class MyExtensions
{
	public static List<T> CustomSort<T, TPropertyType>
	(this IEnumerable<T> collection, string propertyName, string sortOrder)
	{
		List<T> sortedlist = null;
		IQueryable<T> query = collection.AsQueryable<T>();

		ParameterExpression pe = Expression.Parameter(typeof(T), "p");
		Expression<Func<T, TPropertyType>> expr = Expression.Lambda<Func<T, TPropertyType>>(Expression.Property(pe, propertyName), pe);
		
		if (!string.IsNullOrEmpty(sortOrder) && sortOrder == "desc")
			sortedlist = query.OrderByDescending<T, TPropertyType>(expr).ToList();
		else
			sortedlist = query.OrderBy<T, TPropertyType>(expr).ToList();

		return sortedlist;
	}
} 

And invoke the extension method using reflection in the following way:

C#
List<Person> sortedList = typeof(MyExtensions).GetMethod("CustomSort").
	MakeGenericMethod(new Type[] { typeof(Person), sortByPropType }).
	Invoke(persons, new object[] { persons, sortByProp, "asc" }) as List<Person>;

And if necessary, we can still call the extension method with hard-coded types as easily as the following way:

C#
sortedList = persons.CustomSort<Person, int>(sortByProp, "desc");

Edit (08-04-2011)

Thanks to Alois Kraus who suggested that there is no need to convert the IEnumerable<T> to IQueryable<T> since we can call the Compile() method on Expression<TDelegate> object which compiles the lambda expression described by the expression tree into executable code and produces a delegate that represents the lambda expression. We can then pass that delegate to IEnumerable<T>.OrderBy() method. So our revised extension method should be:

C#
public static class MyExtensions
{
	public static List<T> CustomSort<T, TPropertyType>
	(this IEnumerable<T> collection, string propertyName, string sortOrder)
	{
		List<T> sortedlist = null;
		
		ParameterExpression pe = Expression.Parameter(typeof(T), "p");
		Expression<Func<T, TPropertyType>> expr = Expression.Lambda<Func<T, TPropertyType>>(Expression.Property(pe, propertyName), pe);
		
		if (!string.IsNullOrEmpty(sortOrder) && sortOrder == "desc")
			sortedlist = collection.OrderByDescending<T, TPropertyType>(expr.Compile()).ToList();
		else
			sortedlist = collection.OrderBy<T, TPropertyType>(expr.Compile()).ToList();

		return sortedlist;
	}
}

Conclusion

Expression Tree is the coolest language feature C# 3.0 introduced. And along with Lambda Expression, which is also a C# 3.0 feature, Expression Trees can open lots of doors to interesting possibilities. I am definitely looking forward to using these two more and more in my future projects.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Australia Australia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionNice detailed explanation Pin
Shabana Parveen4-May-18 3:55
professionalShabana Parveen4-May-18 3:55 
GeneralExcellent Awesome explanation!! Pin
Vineel K29-Aug-15 19:21
Vineel K29-Aug-15 19:21 
QuestionProblem to convert to AsQueryable Pin
oyechaman28-Oct-14 5:38
oyechaman28-Oct-14 5:38 
GeneralMy vote of 3 Pin
Tony496626-Aug-14 23:46
Tony496626-Aug-14 23:46 
Generalrealy Great and simple Pin
Rasul Gani19-Feb-14 1:45
Rasul Gani19-Feb-14 1:45 
QuestionBrilliant! Pin
SteveBaldwin214-Jan-14 10:03
SteveBaldwin214-Jan-14 10:03 
GeneralRe: Brilliant! Pin
Abul Kayes5-Jan-14 0:16
Abul Kayes5-Jan-14 0:16 
Generalgood article on expression tress. Pin
ramesh_nrk28-Oct-13 22:57
ramesh_nrk28-Oct-13 22:57 
GeneralIt's useful. Pin
Member 101846641-Aug-13 22:03
Member 101846641-Aug-13 22:03 
BugMistake in code? Pin
Riz Thon17-Aug-11 15:20
Riz Thon17-Aug-11 15:20 
GeneralRe: Mistake in code? Pin
Abul Kayes17-Aug-11 18:28
Abul Kayes17-Aug-11 18:28 
GeneralRe: Mistake in code? Pin
AspDotNetDev8-Sep-12 17:33
protectorAspDotNetDev8-Sep-12 17:33 
GeneralRe: Mistake in code? Pin
Abul Kayes20-Sep-12 4:05
Abul Kayes20-Sep-12 4:05 
GeneralGreat article Pin
mr0004714-Aug-11 21:30
mr0004714-Aug-11 21:30 
QuestionSimplification Pin
Richard Deeming11-Aug-11 3:29
mveRichard Deeming11-Aug-11 3:29 
AnswerRe: Simplification Pin
Abul Kayes11-Aug-11 5:59
Abul Kayes11-Aug-11 5:59 
QuestionI did simliar article a while back Pin
Sacha Barber9-Aug-11 22:49
Sacha Barber9-Aug-11 22:49 
QuestionGreat article Pin
pigwin329-Aug-11 11:24
pigwin329-Aug-11 11:24 
AnswerRe: Great article Pin
Abul Kayes11-Aug-11 6:00
Abul Kayes11-Aug-11 6:00 
QuestionUse Dynamic Linq Pin
Daniel Gidman4-Aug-11 8:13
professionalDaniel Gidman4-Aug-11 8:13 
AnswerRe: Use Dynamic Linq Pin
Abul Kayes4-Aug-11 8:24
Abul Kayes4-Aug-11 8:24 
QuestionNice one but Pin
Alois Kraus4-Aug-11 5:45
Alois Kraus4-Aug-11 5:45 
AnswerRe: Nice one but Pin
Abul Kayes4-Aug-11 6:40
Abul Kayes4-Aug-11 6:40 
QuestionNice one [modified] Pin
bilo813-Aug-11 23:46
bilo813-Aug-11 23:46 
AnswerRe: Nice one Pin
Abul Kayes4-Aug-11 5:33
Abul Kayes4-Aug-11 5:33 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.