Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create an Expression<Func<dynamic, dynamic>> - Or is it a bug?

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;
    }
}
like image 379
d.. Avatar asked Jan 12 '10 03:01

d..


1 Answers

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.

like image 185
Eric Lippert Avatar answered Nov 16 '22 01:11

Eric Lippert