convertir un fichier .net Func en une expression .net <Func >

Passer d’un lambda à une expression est facile en utilisant un appel de méthode …

public void GimmeExpression(Expression<Func> expression) { ((MemberExpression)expression.Body).Member.Name; // "DoStuff" } public void SomewhereElse() { GimmeExpression(() => thing.DoStuff()); } 

Mais je voudrais transformer le Func en une expression, seulement dans de rares cas …

 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 ligne qui ne fonctionne pas me donne l’erreur de compilation Cannot implicitly convert type 'System.Func' to 'System.Linq.Expressions.Expression<System.Func>' . Une dissortingbution explicite ne résout pas la situation. Y a-t-il une possibilité de faire cela que je ne vois pas?

Ooh, ce n’est pas facile du tout. Func représente un delegate générique et non une expression. S’il y a un moyen de le faire (à cause des optimisations et d’autres choses faites par le compilateur, certaines données peuvent être jetées, il peut donc être impossible de récupérer l’expression originale), cela désassemblerait l’IL à la volée et en déduisant l’expression (ce qui n’est nullement facile). Traiter les expressions lambda en tant que données ( Expression> ) est une magie effectuée par le compilateur (en gros, le compilateur construit un arbre d’expression dans le code au lieu de le comstackr en IL).

Fait connexe

C’est pourquoi les langages qui poussent les lambda à l’extrême (comme Lisp) sont souvent plus faciles à implémenter en tant qu’interprètes . Dans ces langues, le code et les données sont essentiellement la même chose (même au moment de l’exécution ), mais notre puce ne peut pas comprendre cette forme de code. Nous devons donc émuler une telle machine en choix fait par Lisp comme des langages) ou sacrifiant la puissance (le code ne sera plus exactement égal aux données) dans une certaine mesure (le choix fait par C #). En C #, le compilateur donne l’illusion de traiter le code comme des données en permettant d’interpréter lambdas comme du code ( Func ) et des données ( Expression> ) au moment de la compilation .

Ce que vous devriez probablement faire, c’est de tourner la méthode. Prendre une expression>, et comstackr et exécuter. Si cela échoue, vous avez déjà l’expression à rechercher.

 public void ContainTheDanger(Expression> dangerousCall) { try { dangerousCall().Comstack().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()); } 

De toute évidence, vous devez prendre en compte les implications en termes de performances et déterminer si vous avez vraiment besoin de le faire.

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

Vous pouvez cependant aller dans l’autre sens via la méthode .Comstack (), mais vous ne savez pas si cela vous est utile:

 public void ContainTheDanger(Expression> dangerousCall) { try { var expr = dangerousCall.Comstack(); 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 vous avez parfois besoin d’une expression et que vous avez parfois besoin d’un délégué, vous avez deux options:

  • avoir des méthodes différentes (1 pour chaque)
  • accepte toujours la version Expression<...> , et juste .Comstack().Invoke(...) si vous voulez un délégué. Évidemment, cela a coûté.

JB Evain de l’équipe Cecil Mono fait des progrès pour permettre cela

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 est une bibliothèque qui convertit les delegates en expression

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

Changement

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

À

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