Pasar de una lambda a una expresión es fácil usando una llamada a un método ...
public void GimmeExpression(Expression<Func<T>> expression)
{
((MemberExpression)expression.Body).Member.Name; // "DoStuff"
}
public void SomewhereElse()
{
GimmeExpression(() => thing.DoStuff());
}
Pero me gustaría convertir el Func en una expresión, solo en casos raros ...
public void ContainTheDanger(Func<T> dangerousCall)
{
try
{
dangerousCall();
}
catch (Exception e)
{
// This next line does not work...
Expression<Func<T>> DangerousExpression = dangerousCall;
var nameOfDanger =
((MemberExpression)dangerousCall.Body).Member.Name;
throw new DangerContainer(
"Danger manifested while " + nameOfDanger, e);
}
}
public void SomewhereElse()
{
ContainTheDanger(() => thing.CrossTheStreams());
}
La línea que no funciona me da el error en tiempo de compilación Cannot implicitly convert type 'System.Func<T>' to 'System.Linq.Expressions.Expression<System.Func<T>>'
. Un elenco explícito no resuelve la situación. ¿Hay alguna facilidad para hacer esto que estoy pasando por alto?
at lambda_method(Closure )
a la invocación del delegado compilado.Respuestas:
Oh, no es nada fácil.
Func<T>
representa un genéricodelegate
y no una expresión. Si hay alguna forma de hacerlo (debido a las optimizaciones y otras cosas realizadas por el compilador, es posible que se descarten algunos datos, por lo que puede ser imposible recuperar la expresión original), sería desensamblar el IL sobre la marcha. e inferir la expresión (que de ninguna manera es fácil). Tratar las expresiones lambda como datos (Expression<Func<T>>
) es una magia realizada por el compilador (básicamente, el compilador crea un árbol de expresiones en el código en lugar de compilarlo en IL).Hecho relacionado
Esta es la razón por la que los lenguajes que llevan las lambdas al extremo (como Lisp) suelen ser más fáciles de implementar como intérpretes . En esos lenguajes, el código y los datos son esencialmente lo mismo (incluso en tiempo de ejecución ), pero nuestro chip no puede entender esa forma de código, por lo que tenemos que emular una máquina así construyendo un intérprete sobre ella que la entienda (la elección hecha por Lisp como lenguajes) o sacrificando el poder (el código ya no será exactamente igual a los datos) hasta cierto punto (la elección hecha por C #). En C #, el compilador da la ilusión de tratar el código como datos al permitir que las lambdas se interpreten como código (
Func<T>
) y datos (Expression<Func<T>>
) en tiempo de compilación .fuente
eval
, deberá iniciar el compilador, pero aparte de eso, no hay ningún problema en hacerlo.Expression
sobre su acción de envoltura, pero no tendría información de árbol de expresión sobre los aspectos internos deldangerousCall
delegado.fuente
Func
se ocultará en una nueva expresión. Esto simplemente agrega una capa de datos sobre el código; podría atravesar una capa solo para encontrar su parámetrof
sin más detalles, por lo que está justo donde comenzó.Lo que probablemente debería hacer es cambiar el método. Tome una Expresión>, compile y ejecute. Si falla, ya tiene la expresión para analizar.
Obviamente, debe considerar las implicaciones de rendimiento de esto y determinar si es algo que realmente necesita hacer.
fuente
Sin embargo, puede ir al revés a través del método .Compile (); no estoy seguro de si esto es útil para usted:
fuente
Si a veces necesita una expresión y otras veces necesita un delegado, tiene 2 opciones:
Expression<...>
versión, y solo.Compile().Invoke(...)
si quieres un delegado. Evidentemente esto ha costado.fuente
NJection.LambdaConverter es una biblioteca que convierte delegados en expresión
fuente
fuente
call.Target
parte que me estaba matando. Funcionó durante años, y luego de repente dejó de funcionar y comenzó a quejarse de un bla, bla, bla estático / no estático. ¡Gracias de todos modos!JB Evain del equipo de Cecil Mono está haciendo algunos progresos para permitir esto
http://evain.net/blog/articles/2009/04/22/converting-delegates-to-expression-trees
fuente
Cambio
A
fuente