During my work with expression trees for a few days now, I came across something that I find difficult to understand; hopefully someone will be able so shed some light here.
If you code Expression<Func<dynamic, dynamic>> expr1 = x => 2 * x;
the compiler will complain and you won't get anywhere. However, it seems that if you create one such expression through a method then the compiler seems to be happy about it and the resulting app works. This doesn't make sense, so I'm wondering what goes on behind the curtains.
I suppose that, under the hood, the expression returned by ConvertExpression
is perhaps of type Expression<Func<object, object>>
, which is a valid type, but it puzzles me that I can't use Expression<Func<dynamic, dynamic>>
type in a declaration and yet I can use it as the开发者_运维技巧 return type of a method. See an example below.
Thanks a lot!
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 suppose that, under the hood, the expression returned by ConvertExpression is perhaps of type
Expression<Func<object, object>>
, which is a valid type
Correct.
I can't use
Expression<Func<dynamic, dynamic>>
type in a declaration and yet I can use it as the return type of a method.
This portion of the statement is incorrect. As you note in your example, it is perfectly legal to use that type in the declaration of a local variable.
The bit that is not legal is the execution of a dynamic operation inside a lambda that is being converted to an expression tree type. The specific expression tree type is irrelevant; what matters is that the operation is dynamic.
The compiler error I got when I tried your code was "error CS1963: An expression tree may not contain a dynamic operation". I changed the problem line to Expression<Func<dynamic, dynamic>> expr1 = x => x;
(removing the "operation" from the lambda) and it worked! So you are allowed to have dynamics in expressions, but you can't actually perform any "operations" on them. Not very helpful, I know. In my testing, even .ToString()
counts as an operation.
精彩评论