Assigning a Func to an Expression and vice versa
Solution 1
Regarding to the C# Language Specification a lambda expression like
i => i + i
is an anonymous function. An expression with this classification can be implicitly converted to a compatible delegate type or expression tree type. This is why you can write both
Func<int, int> sumFunc = i => i + i;
Expression<Func<int, int>> sumExp = i => i + i;
The first is a delegate type, the second an expression tree type. Because there is no implicit converting between those types, you cannot assign sumFunc = sumExp
or vice versa. But since the expression tree sumExp
represents a lambda expression, you can compile that expression to an executable delegate and assign it to sumFunc
, because this is such a compatible delegate:
sumFunc = sumExp.Compile();
The other direction is not possible, because a delegate cannot easily be "decompiled" into an expression tree.
The reason why you cannot write
object o = i => i + i;
is, that an anonymous function does not have a value or type in and of itself, it is just convertible to a delegate or expression tree type. You have to tell the compiler which of both you want, so you can convert it at first and then assign the result to a variable of type object
:
object sumFuncObject = (Func<int, int>) (i => i + i);
object sumExpObject = (Expression<Func<int, int>>) (i => i + i);
Regarding your last question: You can create custom implicit or explicit conversions between complex types, so that this "magic" can be applied to assignments. See the Conversion Operations Programming Guide for more information.
Solution 2
Actually both of these expressions are syntax sugar, transformed by compiler in different language constructions.
When you write lambda expression, compiler does this: makes member function that matches your lamda expression and assigns it to your sumFunc variable (this is not exact code, just to get an idea):
class Program
{
private static int generatedname(int i)
{
return i + i;
}
static void Main()
{
Func<int, int> sumFunc = generatedname;
}
}
When you write Expression tree there is even more magic happens. At compile time compiler transforms your expression into Expression tree "construction". Like this.
class Program
{
static void Main()
{
var addPrm = Expression.Parameter(typeof(int), "i");
Expression<Func<int, int>> sumExp =
Expression.Lambda<Func<int, int>>(
Expression.Add(
addPrm,
addPrm
),
addPrm
);
}
}
You see this is completely different things, thats why you can not simply cast one to another.
Mehmet Ataş
Updated on June 12, 2022Comments
-
Mehmet Ataş almost 2 years
I was tampering with Expressions and I got confused at some points
We can assign same LamdaExpression to both Expression and/or Func. But we cannot assign a Func to an Expression (or an Expression to Func). Why cannot we do that? I looked for if a conversion operator between Expression and Func is defined but I could not found any.
Func<int, int> sumFunc = i => i + i; Expression<Func<int, int>> sumExp = i => i + i; // sumExp = sumFunc; // Cannot convert source type 'System.Func<int,int>' to target type 'System.Linq.Expressions.Expression<System.Func<int,int>>' // sumFunc = sumExp; // Cannot convert source type 'System.Linq.Expressions.Expression<System.Func<int,int>>' to target type 'System.Func<int,int>'
Even we cannot assign a LambdaExpression to an object. Again, why cannot we do that?
// object o = i => i + i; // Cannot convert source type 'lambda expression' to target type 'object'
I think there is something about compiler. If so, can we write our custom types those behave in this (confusing) manner and take advantage of something.
-
Yinda Yin over 11 yearsWhy wouldn't it be possible? Surely you can iterate through the abstract syntax tree and generate a recursive collection of objects. Whether that's at all useful is another question.
-
doug65536 over 11 yearsSure it would be possible. There is probably a much better way, but at worst you could reverse engineer the IL (programmatically) and generate an expression tree. But why?
-
doug65536 over 11 yearsI'm pretty sure if you make them a real method (instead of lambda), you can use a single "invoke" node in the expression tree to run the function. If you need arbitrary lambdas, I'm not sure off hand how to do that.