2010-01-12 21 views
9

Durante mi trabajo con árboles de expresión durante unos días, me encontré con algo que encuentro difícil de entender; con suerte alguien podrá así arrojar algo de luz aquí.Cómo crear una expresión <Func <dynamic, dynamic >> - ¿O es un error?

Si codifica Expression<Func<dynamic, dynamic>> expr1 = x => 2 * x;, el compilador se quejará y no obtendrá nada. Sin embargo, parece que si crea una expresión de este tipo a través de un método, entonces el compilador parece estar contento con él y la aplicación resultante funciona. Esto no tiene sentido, así que me pregunto qué sucede detrás de las cortinas.

supongo que, bajo el capó, la expresión devuelta por ConvertExpression es quizás el tipo de Expression<Func<object, object>>, que es un tipo válido, pero me extraña que no puedo usar Expression<Func<dynamic, dynamic>> tipo en una declaración y todavía lo puedo usar como el tipo de devolución de un método. Vea un ejemplo a continuación.

¡Muchas gracias!

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; 
    } 
} 
+0

Dinámica en Expression Trees - Solución aquí: [http://stackoverflow.com/questions/3562088/c-4-dynamic-in-expression-trees](http://stackoverflow.com/questions/3562088/c -4-dynamic-in-expression-trees) –

Respuesta

13

supongo que, bajo el capó, la expresión devuelto por ConvertExpression es quizás de tipo Expression<Func<object, object>>, que es un tipo válido

correcta.

No puedo usar Expression<Func<dynamic, dynamic>> Escriba en una declaración y, sin embargo, puedo usarlo como el tipo de devolución de un método.

Esta parte de la declaración es incorrecta. Como nota en su ejemplo, es perfectamente legal usar ese tipo en la declaración de una variable local.

El bit que no es legal es la ejecución de una operación dinámica dentro de una lambda que se está convirtiendo a un tipo de árbol de expresiones. El tipo de árbol de expresión específico es irrelevante; lo que importa es que la operación sea dinámica.

+0

Muchas gracias por la respuesta y el comentario - ¡Eso lo resuelve! –

+0

@Eric, básicamente, hay (¿todavía?) No hay soporte para las operaciones dinámicas dentro de lambdas. ¿Alguna vez va a haber? – Maghis

+1

@Maghis: StackOverflow es un mal lugar para hacer preguntas que requieren una predicción del futuro. No tenemos planes para tal característica ahora; si lo haremos en los próximos veinte años, ¿cómo debería saberlo? –

3

El error del compilador que obtuve cuando intenté su código era "CS1963 de error: Un árbol de expresión puede no contener una operación dinámica". Cambié la línea del problema a Expression<Func<dynamic, dynamic>> expr1 = x => x; (eliminando la "operación" de la lambda) ¡y funcionó! Entonces, se te permite tener dinámica en las expresiones, pero no puedes realizar ninguna "operación" en ellas. No es muy útil, lo sé. En mi prueba, incluso .ToString() cuenta como una operación.

+0

Gracias - Veo lo mismo ... Eso hace las cosas aún más extrañas, creo - Entonces, si hay algunas operaciones, entonces no compila. Sin embargo, si no hay ninguno, entonces se compila. ¿Eso no contradice la filosofía detrás del tipo dinámico de operaciones de resolución, invocaciones de métodos, etc. en tiempo de ejecución? –

+2

El codegen que generamos para esas operaciones dinámicas en tiempo de compilación que implementa la semántica dinámica en tiempo de ejecución es extremadamente complejo; suficientemente complejo que no hay una manera fácil de representarlo limpiamente en un árbol de expresiones. Básicamente, la elección se redujo a "no permitirlos en los árboles de expresión", o "enviar tarde" o "barco con errores". Elegimos el primero. Siempre podemos agregar esto como una característica en futuras versiones si hay suficiente demanda; Si puede describir los atractivos escenarios del usuario que motivan esta característica, eso ayudará cuando llegue el momento de presupuestar el trabajo de la función. –

+0

Si alguien abre una publicación de conexión en esta, la votaré. –

Cuestiones relacionadas