9

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;
    }
}
svick
  • 236,525
  • 50
  • 385
  • 514
d..
  • 1,063
  • 1
  • 10
  • 18
  • Dynamics in Expression Trees - Solution Here: [http://stackoverflow.com/questions/3562088/c-4-dynamic-in-expression-trees](http://stackoverflow.com/questions/3562088/c-4-dynamic-in-expression-trees) – Ryan D. Hatch Oct 12 '10 at 01:09

2 Answers2

13

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.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • @Eric basically there is (still?) no support for dynamic operations inside lambdas. Is there ever gonna be? – Maghis Feb 09 '12 at 20:15
  • 1
    @Maghis: StackOverflow is a bad place to ask questions that require a prediction of the future. We have no plans for such a feature now; whether we will in the next twenty years, how should I know? – Eric Lippert Feb 09 '12 at 20:47
  • @Eric I am sorry, you misunderstood due to my poor english. I just wanted to know if you had any plans on supporting it, "We have no plans for such a feature now" is exactly what i was looking for, thanks – Maghis Feb 09 '12 at 20:50
3

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.

Tim Danner
  • 630
  • 8
  • 20
  • Thanks - I see the same... That makes things even more bizarre I think - So, if there are some operations, then it doesn't compile. However, if there aren't any, then it compiles. Doesn't that contradict the philosophy behind the dynamic type of resolving operations, method invocations, etc. at runtime? – d.. Jan 12 '10 at 04:00
  • 2
    The codegen that we generate for those dynamic operations at compile time that implements dynamic semantics at runtime is exceedingly complex; sufficiently complex that there is no easy way to represent it cleanly in an expression tree. Basically, the choice came down to "don't allow them in expression trees", or "ship late", or "ship buggy". We chose the former. We can always add this as a feature in future versions if there's enough demand; if you can describe the compelling user scenarios that motivates this feature, that will help when it comes time to budget the feature work. – Eric Lippert Jan 12 '10 at 04:45