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 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.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 (the actual type behind the expression trees).  Unless you’re someone like [Oren](http://ayende.com/) or [Jeremy](http://codebetter.com/blogs/jeremy.miller/) of course.

But if you happen to use an API that works with Expression 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.

Separation of Concerns by example: Part 5