1

Here is the code.

  int[] data = new int[] { 1, 2, 3, 4, 5 };
  var q1 = data.Select(x => 10 * x);
  var q2 = data.AsQueryable().Select(x => 10 * x);
  Expression<Func<int,int>> qe = (x) => 10 * x;

In the first case the compiler generates code to evaluate the expression. There is no expression tree in the output.

In the second it generates an expression tree (visible on debug), which at run-time is compiled and executed to perform the query (and does exactly the same thing).

In the third case, the same lambda as (2) is created directly as an expression tree (not as code).

What makes the compiler generate an expression tree instead of code in these two cases, and are there any other interesting cases?

The reason: I want to 'pick apart' the top level of the expression tree at runtime, and then compile and execute the lower levels. I'm having trouble getting the compiler to do things my way!

5
  • 2
    It's a case of "that's how the compiler is written". It needs to do it that way to make expressions work. It's just like methods that return IEnumerable<T> can use yield return. Special compiler operations to support language features. Commented Aug 17, 2016 at 4:38
  • The list goes on. The compiler treats Nullable<T> types differently. It can use duck-typing for foreach loops. LINQ queries are really just re-arranged methods (usually extension method, but don't need to be). using statements only work with disposables, etc. Commented Aug 17, 2016 at 4:44
  • Your first statement isn't really an expression; it's a constructor call. The two Select statements are Linq statements which produce expressions as part of their normal operation. The fourth statement generates an expression because you specified an expression as the resulting type. Commented Aug 17, 2016 at 4:54
  • Very unclear what you have problem with - if code asks for expression (like Queryable.Select you use) than obviously compiler will construct expression tree... So what exactly you have problem with? Commented Aug 17, 2016 at 5:15
  • Thanks all but none of these answer the question: what is the critical feature that decides which path the compiler will take for a lambda? Commented Aug 17, 2016 at 14:24

1 Answer 1

3

Enumerable's Select method accepts a parameter of type Func<TSource,TResult>. Queryable's Select method accepts a parameter of type Expression<Func<TSource,TResult>>.

It's as simple as that - if the compiler has a lambda expression it can either generate an expression or compile the lambda, and that decision is based on what it's being asked to create - if it's an Expression of some type, it will generate the expression tree. If it's a Func or other delegate type, it generates the code.

Sign up to request clarification or add additional context in comments.

2 Comments

Damn! It really is that simple! The compiler generates code if the parameter derives from Delegate and an expression tree if it derives from Expression, and there are no other choices. Thanks!
Just bear in mind that delegates can span to multiple lines but an expression tree which is written the same way as a delegate e.g. Expression<Func<int[]>> lambda = () => new int[] { 1, 2, 3, 4, 5 }; can only be one line.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.