2

Actually, four related questions:

1) Why is it possible to do this?

Expression<Func<int, int>> incrementorExpression = (i => i + 1);

But to not be able to do this?

LambdaExpression decrementorExpression = (i => i - 1);

In the second case, the compiler reports thus: "Cannot convert lambda expression to type 'System.Linq.Expressions.LambdaExpression' because it is not a delegate type"

2) Where is the cast between TDelegate and Expression<TDelegate> declared? I think I remember having seen it in the past but cannot seem to find it just now. But I can't be sure whether I saw it or not.

3) When I do this:

Expression<Func<int, int>> incrementExpression = (i => ++i);

The compiler says, "An expression tree may not contain an assignment operator." Why is that so?

4) And then if I can do this:

Expression<Func<int, int>> incrementorExpression = (i => i + 1);

Then why can't I do this?

public Expression<Func<T>> ToExpression<T>(Func<T> func)
{
  return func;
}
Community
  • 1
  • 1
Water Cooler v2
  • 32,724
  • 54
  • 166
  • 336

2 Answers2

7

You almost found the answer already.

i => i + 1 is not a Func<int, int>. It is a lambda expression. Lambda expressions can be converted to a matching delegate type, or a matching expression tree type.

If a lambda expression is converted to a delegate type, the compiler compiles this into IL code that has the specified effect.

If a lambda expression is converted to an expression tree type, the compiler compiles this into IL that generates an expression tree that represents what you wrote in your lambda expression. Expression trees are meant for later parsing by libraries, so it's important for the expression tree to closely match the code you wrote.

Func<int, int> f = i => i + 1; // okay, creates delegate.
Expression<Func<int, int>> e = i => i + 1; // okay, creates expression tree.

No information can be retrieved from f about the operation it performs. It is known from its delegate type that it takes an int and returns an int, but other than that, it is a black box. You put a number in, and you get a number out, but you no longer know how.

e, on the other hand, stores the fact that 1 is added to a parameter named i, and even that 1 appears on the RHS of the +.

This extra information that is stored in e is often essential to those libraries that interpret expression trees, so an implicit conversion from Func<int, int> to Expression<Func<int, int>> just wouldn't work: the required information is no longer available.

As for LambdaExpression decrementorExpression = (i => i - 1);, that is invalid simply because there is no way for the compiler to determine whether you want an Expression<Func<int, int>>, an Expression<Func<int, object>>, or an Expression<MyFunc> where MyFunc is a custom delegate type you created.

And finally "An expression tree may not contain an assignment operator", that's mainly because for the intended use cases of expression trees, it is generally not meaningful for the expression to contain an assignment. It is a somewhat arbitrary restriction, though, considering that .NET expression trees are capable of representing assignment operations.

0

Here are some thought fragments that are in my mind and they help resolve some of the mystery. But I am still waiting for a complete answer.

First of all, i => i + 1 is not a Func<int, int> or a Func anything. The latter is IL; Func is IL; TDelegate is IL. There is no way from IL back to expression.

The former is actually an expression. It is some textual representation of the lamda expression.

Now, even if it is so, there must be some cast somewhere from this type of text type of thing i => i + 1 to an Expression<TDelegate>?

But I guess there isn't because there is no syntactical token to represent the text i => i + 1. There must be some class representing this in the compiler DOM but there isn't anything in the LINQ Expression API for this sort of a thing. And so the compiler uses its own AST to know, "Oh, I just met with this sort of a piece of text. Let me parse it and turn it into an Expression."

But that's all I have, which kind of, if it is true, answers only a part of my question. Still waiting for the rest of the puzzle to be solved.

Water Cooler v2
  • 32,724
  • 54
  • 166
  • 336