Expressions and Lambdas


Some conversation on a recent post on Chad's blog brought up the confusion between Lambdas and Expressions.  A while back, I went into the various ways to create delegates throughout the different versions of C#.  Although I touched on it briefly, the slight variations in the different lambda types can lead to some unexpected compilation errors.

C# 3.0 introduced two types of lambdas: Expression Lambdas and Statement Lambdas.  The difference is easy to spot, here are two identically functioning lambdas:

public void LambdaExpressionsAndStatements()
{
    var books = GetBooks();

    var exprBooks = books.Find(book => book.Author.Contains("Fowler"));

    var stmtBooks = books.Find(book => { return book.Author.Contains("Fowler"); });
}

See the difference?  The second lambda has brackets.  These brackets are a statement block, and can contain any old C# code.  The statement lambda is really just a shorter version of a C# 2.0 anonymous method.

The first version is a different type of lambda: the expression lambda.  So why do both compile?  The Find method's signature is:

public T Find(Predicate<T> match)

Predicate<T> is a delegate type, so how does the "book.Author.Contains" part of the first lambda get converted to a delegate?

It turns out that the C# compiler is really smart.  Smart enough to see an expression that returns a boolean, which matches the signature of the Predicate delegate.  At compile time, it creates an anonymous delegate from the expression, as confirmed by Reflector.  So we couldn't do something like this:

var exprBooks = books.Find(book => book.Author.Split(' '));

We get the compiler error:

Cannot convert lambda expression to delegate type 'System.Predicate<Samples.Book>' because some of the return types in the block are not implicitly convertible to the delegate return type

Basically, that the return type of the Split call (string[]) isn't boolean, so it doesn't compile.

Expressions

Expressions are a fundamental addition to C# that allows LINQ to work its magic.  Expressions at compile-time are converted to expression trees, which is really a large object made up of things like equals statements, variables, etc.  It's as if you decomposed C# statements into their fundamental building blocks, and represented these building blocks as classes and objects.

The interesting thing about expressions is that they can be converted to lambdas, and therefore executable code.  Lambda statements however, don't jive with expressions.  Since lambda statements contain actual blocks of code, rather than an expression that represents a block of code, the compiler can't convert all of those potential lines of code of a statement block into a real-deal expression.  It's why you can't do this in a LINQ query expression:

var linqBooks = from book in books
                where { return books.Author.Contains("Fowler"); }
                select book;

I get a nasty compile error:

Invalid expression term '{'

I tried to use a lambda statement (the bracket business) where a lambda expression was required.  A LINQ query expression is compiled into an expression tree, mixed in with the extension method calls to the LINQ query extensions (Where, Select, Union etc.)  Here's another way to write the above LINQ query expression, using LINQ query extensions instead:

var linqStmtBooks = books.Where(book => { return book.Author.Contains("Fowler"); });

This looks exactly like our original Find example above, but this time I'm using the LINQ query extension method, instead of the List<T>.Find method.  But LINQ query expressions (the SQL-like from..where..select) requires expressions, not statements.

Why do I care?

Well, 99.999% of the time, you won't.  Most folks won't develop any APIs that use Expression<T> (the actual type behind the expression trees).  Unless you're someone like Oren or Jeremy of course.

But if you happen to use an API that works with Expression<T> instead of Func (delegates), you'll need to care. For example, LINQ to SQL, LINQ to NHibernate and Entity Framework all deal with Expressions, not Funcs.  So you'll need to use the lambda expressions instead of the lambda statements.  No brackets allowed!

So if you're dealing with LINQ query expressions (the "from..where..select" business), you'll have to go out of your way to do LINQ query statements with brackets.  But using other APIs, you'll get a strange compile error.  If you see this compile error, just use an expression instead of a statement, and you'll be set.


Posted Jul 18 2008, 10:59 PM by bogardj
Filed under:

Comments

Paul Batum wrote re: Expressions and Lambdas
on 07-19-2008 4:00 AM

Hi Jimmy,

Thanks for the post! It helped me discover some flaws in my reasoning.

Sam Gentile The World According to MSCOREE wrote New and Notable 253
on 07-19-2008 8:08 AM

C#/.NET Programming/WPF Jimmy has a nice post on the differences between Expressions and Lambdas Scott

Dew Drop - July 19, 2008 | Alvin Ashcraft's Morning Dew wrote Dew Drop - July 19, 2008 | Alvin Ashcraft's Morning Dew
on 07-19-2008 8:26 AM

Pingback from  Dew Drop - July 19, 2008 | Alvin Ashcraft's Morning Dew

Reflective Perspective - Chris Alcock » The Morning Brew #140 wrote Reflective Perspective - Chris Alcock &raquo; The Morning Brew #140
on 07-21-2008 2:17 AM

Pingback from  Reflective Perspective - Chris Alcock  &raquo; The Morning Brew #140

Sam Gentile's Blog wrote New and Notable 253
on 12-02-2008 3:27 PM

C#/.NET Programming/WPF Jimmy has a nice post on the differences between Expressions and Lambdas Scott continues to learn WPF Learning WPF with BabySmash - Customer Feedback and a WPF Font ComboBox Learning WPF with BabySmash and Back to Basics - Making

Add a Comment

(required)  
(optional)
(required)  
Remember Me?

Enter the numbers above:
Copyright Los Techies 2008, 2009. All rights reserved.
Powered by Community Server (Commercial Edition), by Telligent Systems