expressions expresiones c# .net lambda expression func

c# - expresiones - convertir.net Func<T> a.net Expression<Func<T>>



linq lambda c# (9)

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 lanzamiento explícito no resuelve la situación. ¿Hay alguna posibilidad de hacer esto que estoy pasando por alto?


Cambio

// This next line does not work... Expression<Func<T>> DangerousExpression = dangerousCall;

A

// This next line works! Expression<Func<T>> DangerousExpression = () => dangerousCall();



Lo que probablemente deberías hacer es cambiar el método. Tome una Expresión> y compile y ejecute. Si falla, ya tienes la Expresión para mirar.

public void ContainTheDanger(Expression<Func<T>> dangerousCall) { try { dangerousCall().Compile().Invoke();; } catch (Exception e) { // This next line does not work... var nameOfDanger = ((MemberExpression)dangerousCall.Body).Member.Name; throw new DangerContainer( "Danger manifested while " + nameOfDanger, e); } } public void SomewhereElse() { ContainTheDanger(() => thing.CrossTheStreams()); }

Obviamente, debe considerar las implicaciones de rendimiento de esto y determinar si es algo que realmente necesita hacer.


Ooh, no es nada fácil. Func<T> representa un delegate genérico y no una expresión. Si hay alguna forma de hacerlo (debido a las optimizaciones y otras cosas hechas por el compilador, algunos datos podrían descartarse, por lo que podría ser imposible recuperar la expresión original), estaría desensamblando el IL sobre la marcha e inferir la expresión (que de ninguna manera es fácil). El tratamiento de expresiones lambda como datos ( Expression<Func<T>> ) es una magia hecha por el compilador (básicamente el compilador construye un árbol de expresiones en código en lugar de compilarlo en IL).

Hecho relacionado

Esta es la razón por la cual los lenguajes que llevan a las lambdas al extremo (como Lisp) a menudo son más fáciles de implementar como intérpretes . En esos idiomas, 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, así que tenemos que emular esa máquina construyendo un intérprete encima que la entienda (el elección hecha por Lisp como idiomas) o sacrificar la potencia (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 lambdas se interprete como código ( Func<T> ) y datos ( Expression<Func<T>> ) en tiempo de compilación .


Si a veces necesita una expresión y algunas veces necesita un delegado, tiene 2 opciones:

  • tener diferentes métodos (1 para cada uno)
  • siempre acepte la versión Expression<...> y solo .Compile().Invoke(...) si desea un delegado. Obviamente, esto ha costado.

Sin embargo, puedes ir por el otro lado a través del método .Compile (): no estoy seguro si esto es útil para ti:

public void ContainTheDanger<T>(Expression<Func<T>> dangerousCall) { try { var expr = dangerousCall.Compile(); expr.Invoke(); } catch (Exception e) { Expression<Func<T>> DangerousExpression = dangerousCall; var nameOfDanger = ((MethodCallExpression)dangerousCall.Body).Method.Name; throw new DangerContainer("Danger manifested while " + nameOfDanger, e); } } public void SomewhereElse() { var thing = new Thing(); ContainTheDanger(() => thing.CrossTheStreams()); }


NJection.LambdaConverter es una biblioteca que convierte delegados en expresión

public class Program { private static void Main(string[] args) { var lambda = Lambda.TransformMethodTo<Func<string, int>>() .From(() => Parse) .ToLambda(); } public static int Parse(string value) { return int.Parse(value) } }


private static Expression<Func<T, bool>> FuncToExpression<T>(Func<T, bool> f) { return x => f(x); }


Expression<Func<T>> ToExpression<T>(Func<T> call) { MethodCallExpression methodCall = call.Target == null ? Expression.Call(call.Method) : Expression.Call(Expression.Constant(call.Target), call.Method); return Expression.Lambda<Func<T>>(methodCall); }