Introduction
This article is a short article on the Expressions namespace and how to create Expression Trees manually.
What I'm going to attempt to cover in this article will be:
I think that's quite enough for one article.
.NET has evolved over the years, but one of the constants has been the use of delegates. For those that don't know what delegates are, they are simply method pointers. Now although not directly related to this article's content, I think it is a good idea to show you a bit about the evolution of delegates leading to lambdas in order for you to understand the rest of the article.
Now it used to be that you had to manually create delegates such as this:
public delegate void ProcessBookDelegate(Book book);
public class BookDB
{
public void ProcessPaperbackBooks(ProcessBookDelegate processBook)
{
foreach (Book b in list)
{
if (b.Paperback)
processBook(b);
}
}
}
class TestBookDB
{
static void PrintTitle(Book b)
{
System.Console.WriteLine(" {0}", b.Title);
}
static void Main()
{
bookDB.ProcessPaperbackBooks(PrintTitle);
}
}
Blatantly stolen from MSDN (http://msdn.microsoft.com/en-us/library/ms173176(VS.80).aspx).
Luckily over the years, Microsoft has given us anonymous delegates which allow us to do things like this:
this.Loaded += delegate
{
MessageBox.Show("in the delegate");
};
Where the signature of this delegate would be something like the following if we had to actually declare it in a non anonymous manner:
internal delegate void MessagDelegate();
Which is all well and good, but sometimes you want the ability to create an anonymous delegate that takes a parameter, or maybe even returns a value. Luckily we can still do this using an anonymous delegate, but we must make sure that the delegate type is known at runtime, so we have to provide a non anonymous delegate signature to use within any method that accepts an anonymous delegate. This allows us to get the correct return type/value.
Here is an example:
internal delegate String UpperCaseAStringDelegate(String s);
....
....
this.Loaded += delegate
{
ShowIt(delegate(string s)
{
MessageBox.Show(s);
return s.ToUpper();
});
};
....
....
private void ShowIt(UpperCaseAStringDelegate del)
{
MessageBox.Show(del("hello"));
}
Now a little bit of time goes by and Microsoft introduced the lambda syntax. There are many great articles on lambdas. I will just show one small example comparing them to anonymous delegates, but you will find more on the internet, have a look.
Using the last example, we could simply replace the delegate(string s) {...}
with a lambda, which would be (s) => {...}
. Here is a reworked example using the lambda syntax:
internal delegate String UpperCaseAStringDelegate(String s);
....
....
this.Loaded += delegate
{
ShowIt((s) =>
{
MessageBox.Show(s);
return s.ToUpper();
});
};
....
....
private void ShowIt(UpperCaseAStringDelegate del)
{
MessageBox.Show(del("hello"));
}
But we can actually go one step further and remove the reliance on the UpperCaseAStringDelegate
altogether. We can simply replace it with one of the generic Func<T,TResult>
delegates where we would then end up with the following code:
internal delegate String UpperCaseAStringDelegate(String s);
....
....
this.Loaded += delegate
{
ShowItLambda((s) =>
{
MessageBox.Show(s);
return s.ToUpper();
});
};
....
....
private void ShowItLambda(Func<String,String> lambda)
{
MessageBox.Show(lambda("hello"));
}
Isn't that nicer. That's all I wanted to say in this section, I just wanted you to understand what the Func<T,TResult>
delegates and lambdas are actually doing before we move on to some of the more finer details of the Creating Expressions and the Expression namespace.
Now that we know about lambdas and some of the Func<T,TResult>
delegates, let's proceed to look at Expressions. Expressions came with lambdas. Microsoft has this to say about Expression Trees "Expression trees represent language-level code in the form of data. The data is stored in a tree-shaped structure. Each node in the expression tree represents an expression, for example a method call or a binary operation such as x < y."
So what does that mean to us. Well let's consider the following diagram, where we have one of the generic Func<T,T,TResult>
delegates, which means we have a lambda that takes 2 parameters and returns a value. The following illustration shows an example of an expression and its representation in the form of an expression tree.
So you can see from this that we have things like BinaryExpression
/ParameterExpression
/MemberExpression
. Ok, fine. But what would a concrete example look like.
I think the best way to do this is for me to show you some examples. I will show you two examples, all based on the following test data:
String[] bindings = new String[2] {"NetTcpBinding", "HttpBinding"};
List<Order> orders = new List<Order>{
new Order {OrderId=1},
new Order {OrderId=2}
};
Example 1
Let's consider the following:
foreach (String bindingString in
bindings.Where((x) => x.StartsWith("Net")))
{
Console.WriteLine("Yields {0}".F(bindingString));
}
This simply uses a lambda within a Where
extension method. But could we do this differently, well yeah we could also do the following, where we actually have an Expression<Func<String, Boolean>>
variable, which we MUST compile when it is used in the where
extension method. Do you see where all this is going?
Expression<Func<String, Boolean>>
staticDeclaredExpression = (x) => x.StartsWith("Net");
foreach (String bindingString in
bindings.Where(staticDeclaredExpression.Compile()))
{
Console.WriteLine("Yields {0}".F(bindingString));
}
Ok now that you understand the basic idea behind Expressions, maybe it is time to see some harder examples.
Example 2
In this example, we will be manually creating a lambda that will do the following (o) => o.OrderID==2
to use within a where
extension method. Here is the code:
ConstantExpression constOrderId = Expression.Constant(2);
ParameterExpression paramOrder =
Expression.Parameter(typeof(Order), "o");
MemberExpression mex =
LambdaExpression.PropertyOrField(paramOrder, "OrderId");
BinaryExpression filter = Expression.Equal(mex, constOrderId);
Expression<Func<Order, bool>> exprLambda =
Expression.Lambda<Func<Order, bool>>(filter,
new ParameterExpression[] { paramOrder });
foreach (Order o in orders.Where(exprLambda.Compile()))
{
......
}
So a little explanation here, as we are using an object and want to use one of its methods, we need to use a MemberExpression
, and as we need to test this against a constant, we need to use a ConstantExpression
. We also need to test for equality, so we need to use a BinaryExpression
. Finally we need to create an overall Expression Tree, which we can do using the Expression.Lambda<Func<T,TResult>>
. If you look at the Expression.Lambda
method, it may begin to make a bit more sense:
Example 3
The last example will be trying to do the following (x) => x.Length > 1
where x
is a String
:
ConstantExpression constLength = Expression.Constant(1);
ParameterExpression stringParameter =
Expression.Parameter(typeof(String), "s");
MemberExpression stringMember =
LambdaExpression.PropertyOrField(stringParameter, "Length");
Expression<Func<String, Boolean>> bindLambda =
Expression.Lambda<Func<String, Boolean>>
(
Expression.GreaterThan(
stringMember,
constLength),
stringParameter
);
foreach (String bindingString in
bindings.Where(bindLambda.Compile()))
{
....
}
This is much the same as the last example, with the exception that I am building the entire tree using the following Expression.Lambda()
method, where I am effectively building the entire Expression Tree in 1 hit.
public static Expression<TDelegate> Lambda<TDelegate>(
Expression body,
IEnumerable<ParameterExpression> parameters
)
If we have a look at some of the classes available within the System.Linq.Expressions
namespace, it gives us a good idea of what could be achieved programmatically.
Covering all these classes is outside the scope of what I wanted to cover in this article. If you want to do more, go for it. Have fun.
We're Done
If you liked the article please vote for it. Thanks.
History
- 1st November, 2008: Initial post