Click here to Skip to main content
15,867,308 members
Articles / Programming Languages / C#

A Journey into Expressions

Rate me:
Please Sign up or sign in to vote.
4.90/5 (64 votes)
1 Nov 2008CPOL5 min read 167.6K   423   159   52
A journey into Expressions

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.

A Little History

.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:

C#
// Declare a delegate type for processing a book:
public delegate void ProcessBookDelegate(Book book);

// Maintains a book database.
public class BookDB
{
    // Call a passed-in delegate on each paperback book to process it:
    public void ProcessPaperbackBooks(ProcessBookDelegate processBook)
    {
        foreach (Book b in list)
        {
            if (b.Paperback)
                // Calling the delegate:
                processBook(b);
        }
    }
}

// Class to test the book database:
class TestBookDB
{
    // Print the title of the book.
    static void PrintTitle(Book b)
    {
        System.Console.WriteLine("   {0}", b.Title);
    }

    // Execution starts here.
    static void Main()
    {
        // Create a new delegate object associated with the static
        // method Test.PrintTitle:
        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:

C#
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:

C#
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:

C#
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:

C#
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:

C#
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.

Creating Expressions

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.

Image 1

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:

C#
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:

C#
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?

C#
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:

C#
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:

Image 2

Example 3

The last example will be trying to do the following (x) => x.Length > 1 where x is a String:

C#
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.

C#
public static Expression<TDelegate> Lambda<TDelegate>(
    Expression body,
    IEnumerable<ParameterExpression> parameters
)

Expression Namespace

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.

Image 3

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

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)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)

- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence

Both of these at Sussex University UK.

Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2016
  • Codeproject MVP 2016
  • Microsoft C# MVP 2015
  • Codeproject MVP 2015
  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions

 
PraiseVote of 5 Pin
BrettAn3-Jul-18 3:57
BrettAn3-Jul-18 3:57 
GeneralMy vote of 5 Pin
JasonShenW1-Jun-15 14:26
JasonShenW1-Jun-15 14:26 
like it
GeneralVery Nice! Pin
Phoenix Roberts17-Apr-12 3:14
Phoenix Roberts17-Apr-12 3:14 
GeneralMy vote of 5 Pin
Eric Xue (brokensnow)7-Sep-10 17:46
Eric Xue (brokensnow)7-Sep-10 17:46 
GeneralRe: My vote of 5 Pin
Sacha Barber4-Aug-11 0:38
Sacha Barber4-Aug-11 0:38 
GeneralCool Pin
Hristo-Bojilov29-Jul-09 6:35
Hristo-Bojilov29-Jul-09 6:35 
NewsMessage Closed Pin
30-May-09 16:42
Gilad Khen30-May-09 16:42 
GeneralImplement sql method Like Pin
Mr.Pjotrek18-Mar-09 8:15
Mr.Pjotrek18-Mar-09 8:15 
GeneralRe: Implement sql method Like Pin
Sacha Barber18-Mar-09 11:11
Sacha Barber18-Mar-09 11:11 
GeneralRe: Implement sql method Like Pin
Mr.Pjotrek18-Mar-09 23:44
Mr.Pjotrek18-Mar-09 23:44 
GeneralRe: Implement sql method Like Pin
Don Kackman11-Jul-09 5:39
Don Kackman11-Jul-09 5:39 
GeneralRe: Implement sql method Like Pin
Sacha Barber11-Jul-09 6:29
Sacha Barber11-Jul-09 6:29 
GeneralGreat introduction of LINQ Expressions Pin
hzx12330-Jan-09 6:04
hzx12330-Jan-09 6:04 
GeneralRe: Great introduction of LINQ Expressions Pin
Sacha Barber30-Jan-09 21:12
Sacha Barber30-Jan-09 21:12 
GeneralRe: Great introduction of LINQ Expressions Pin
hzx1234-Feb-09 7:42
hzx1234-Feb-09 7:42 
GeneralRe: Great introduction of LINQ Expressions Pin
Sacha Barber4-Feb-09 9:24
Sacha Barber4-Feb-09 9:24 
GeneralGlad to see this article Pin
arhoads7610-Nov-08 3:39
arhoads7610-Nov-08 3:39 
GeneralRe: Glad to see this article Pin
Sacha Barber10-Nov-08 9:40
Sacha Barber10-Nov-08 9:40 
GeneralOne use of lambda expression generation Pin
jpbochi4-Nov-08 5:47
professionaljpbochi4-Nov-08 5:47 
GeneralRe: One use of lambda expression generation Pin
jpbochi4-Nov-08 5:51
professionaljpbochi4-Nov-08 5:51 
GeneralRe: One use of lambda expression generation Pin
Sacha Barber4-Nov-08 6:18
Sacha Barber4-Nov-08 6:18 
GeneralRe: One use of lambda expression generation Pin
Sacha Barber4-Nov-08 6:26
Sacha Barber4-Nov-08 6:26 
GeneralCall me stupid.. Pin
ezazazel3-Nov-08 9:47
ezazazel3-Nov-08 9:47 
GeneralRe: Call me stupid.. Pin
Sacha Barber3-Nov-08 10:29
Sacha Barber3-Nov-08 10:29 
Generallamda =&gt; lambda Pin
tekener2-Nov-08 22:18
tekener2-Nov-08 22:18 

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.