convertir .net Func a .net Expression <Func >

Pasar de una lambda a una expresión es fácil usando una llamada a un método …

public void GimmeExpression(Expression<Func> 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 dangerousCall) { try { dangerousCall(); } catch (Exception e) { // This next line does not work... Expression<Func> 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 comstackción. Cannot implicitly convert type 'System.Func' to 'System.Linq.Expressions.Expression<System.Func>' . Un lanzamiento explícito no resuelve la situación. ¿Hay alguna posibilidad de hacer esto que estoy pasando por alto?

Ooh, no es nada fácil. Func 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 comstackdor, 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> ) es una magia hecha por el comstackdor (básicamente el comstackdor construye un árbol de expresiones en código en lugar de comstackrlo 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 comstackdor da la ilusión de tratar el código como datos al permitir que lambdas se interprete como código ( Func ) y datos ( Expression> ) en tiempo de comstackción .

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> 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.

  private static Expression> FuncToExpression(Func f) { return x => f(x); } 

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(Expression> dangerousCall) { try { var expr = dangerousCall.Compile(); expr.Invoke(); } catch (Exception e) { Expression> 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()); } 

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.

JB Evain del equipo Cecil Mono está haciendo algunos progresos para permitir esto

http://evain.net/blog/articles/2009/04/22/converting-delegates-to-expression-trees

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

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

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

Cambio

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

A

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