How to create an expression <Func <dynamic, dynamic >> - Or is it a mistake?
During my work with expression trees for several days, I came across something that I find difficult to understand; I hope someone can shed light here.
If you code Expression<Func<dynamic, dynamic>> expr1 = x => 2 * x; , the compiler will complain and you won’t get anything. However, it seems that if you create one such expression using a method, then the compiler seems to be pleased with it, and the resulting application works. This does not make sense, so I wonder what happens behind the curtains.
I suggest that under the hood, the expression returned by ConvertExpression may be of type Expression<Func<object, object>> , which is a valid type, but it puzzles me that I cannot use the type Expression<Func<dynamic, dynamic>> in declaration and but I can use it as a return type of a method. See the example below.
Thank you so much!
public class ExpressionExample { public void Main() { // Doesn't compile: //Expression<Func<dynamic, dynamic>> expr1 = x => 2 * x; // Compiles and works - OK Expression<Func<double, double>> expr2 = x => 2 * x; Func<double, double> func2 = (Func<double, double>)expr2.Compile(); Console.WriteLine(func2(5.0).ToString()); // Outputs 10 // Compiles and works - ??? This is the confusing block... Expression<Func<dynamic, dynamic>> expr3 = ConvertExpression(expr2); Func<dynamic, dynamic> func3 = (Func<dynamic, dynamic>)expr3.Compile(); Console.WriteLine(func3(5.0).ToString()); // Outputs 10 // Side note: compiles and works: Expression<Func<object, object>> expr4 = x => double.Parse(2.ToString()) * double.Parse(x.ToString()); Func<object, object> func4 = (Func<object, object>)expr4.Compile(); Console.WriteLine(func4(5.0).ToString()); // Outputs 10 } private Expression<Func<dynamic, dynamic>> ConvertExpression<TInput, TOutput>(Expression<Func<TInput, TOutput>> expression) { Expression<Func<object, TInput>> convertToInput = value => (TInput)value; // The following doesn't compile: var input = Expression.Parameter(typeof(dynamic), "input"); var input = Expression.Parameter(typeof(object), "input"); Expression<Func<TOutput, dynamic>> convertToOutput = value => (dynamic)value; var body = Expression.Invoke(convertToOutput, Expression.Invoke(expression, Expression.Invoke(convertToInput, input))); var lambda = Expression.Lambda<Func<dynamic, dynamic>>(body, input); return lambda; } } I suggest that under the hood, the expression returned by ConvertExpression may be of type
Expression<Func<object, object>>, which is a valid type
Right.
I cannot use the
Expression<Func<dynamic, dynamic>>in the declaration, and yet I can use it as the return type of the method.
This part of the statement is incorrect. As you noted in your example, it is legal to use this type in a local variable declaration.
A bit that is not legal is the execution of a dynamic operation inside a lambda that converts to an expression tree type. The specific type of expression tree does not matter; it is important that the operation is dynamic.
The compiler error you received while trying your code was "error CS1963: expression tree cannot contain dynamic operation". I changed the problem line to Expression<Func<dynamic, dynamic>> expr1 = x => x; (removing the "operation" from lambda) and it worked! Thus, you are allowed to have dynamics in expressions, but you cannot actually perform any “operations” on them. This is not very useful to me. In my testing, even .ToString() is considered an operation.