conversione di .net Func in un’espressione .net <Func >

Passare da un lambda a un’espressione è facile usando una chiamata al metodo …

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

Ma vorrei trasformare il Func in un’espressione, solo in rari casi …

 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 riga che non funziona mi dà l’errore di compilazione Cannot implicitly convert type 'System.Func' to 'System.Linq.Expressions.Expression<System.Func>' . Un cast esplicito non risolve la situazione. C’è una struttura per fare questo che sto trascurando?

Ooh, non è affatto facile. Func rappresenta un delegate generico e non un’espressione. Se c’è un modo per farlo (a causa di ottimizzazioni e altre cose fatte dal compilatore, alcuni dati potrebbero essere gettati via, quindi potrebbe essere imansible riportare l’espressione originale), sarebbe smontare l’IL al volo e inferendo l’espressione (che non è affatto facile). Trattare espressioni lambda come dati ( Expression> ) è una magia fatta dal compilatore (in pratica il compilatore costruisce un albero di espressioni in codice invece di compilarlo in IL).

Fatto correlato

Questo è il motivo per cui le lingue che spingono all’estremo lambdas (come Lisp) sono spesso più facili da implementare come interpreti . In quei linguaggi, codice e dati sono essenzialmente la stessa cosa (anche in fase di esecuzione ), ma il nostro chip non può capire quella forma di codice, quindi dobbiamo emulare tale macchina costruendo un interprete su di esso che lo capisca (il scelta fatta da Lisp come lingue) o sacrificare la potenza (il codice non sarà più esattamente uguale ai dati) in una certa misura (la scelta fatta da C #). In C #, il compilatore dà l’illusione di trattare il codice come dati consentendo di interpretare lambda come codice ( Func ) e dati ( Expression> ) in fase di compilazione .

Quello che probabilmente dovresti fare, è girare il metodo. Prendere in un’espressione>, e compilare ed eseguire. Se fallisce, hai già l’espressione da esaminare.

 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()); } 

Ovviamente è necessario considerare le implicazioni di prestazioni di questo e determinare se è qualcosa che è davvero necessario fare.

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

Puoi comunque andare dall’altra parte con il metodo .Compile () – non è sicuro se questo sia utile per te:

 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()); } 

Se a volte hai bisogno di un’espressione e talvolta hai bisogno di un delegato, hai 2 opzioni:

  • avere metodi diversi (1 per ciascuno)
  • accetta sempre la versione Expression<...> e solo .Compile().Invoke(...) se vuoi un delegato. Ovviamente questo ha un costo.

JB Evain del team Cecil Mono sta facendo dei progressi per abilitare questo

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 è una libreria che converte i delegati in espressioni

 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) } } 

Modificare

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

A

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