Sto creando un albero di espressioni e c'è una situazione in cui ho bisogno di creare un lambda in un altro lambda e memorizzare quello interno in una classe e aggiungere quella classe nell'albero delle espressioni. Questo è semplice esempio di quello che sto cercando di fare (il codice non compila):Albero espressione - compila lambda interno in lambda esterno - risoluzione dell'oscilloscopio
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
namespace SimpleTest {
public class LambdaWrapper {
private Delegate compiledLambda;
public LambdaWrapper(Delegate compiledLambda) {
this.compiledLambda = compiledLambda;
}
public dynamic Execute() {
return compiledLambda.DynamicInvoke();
}
}
public class ForSO {
public ParameterExpression Param;
public LambdaExpression GetOuterLambda() {
IList<Expression> lambdaBody = new List<Expression>();
Param = Expression.Parameter(typeof(object), "Param");
lambdaBody.Add(Expression.Assign(
Param,
Expression.Constant("Value of 'param' valiable"))
);
lambdaBody.Add(Expression.Call(
null,
typeof(ForSO).GetMethod("Write"),
Param)
);
Delegate compiledInnerLambda = GetInnerLambda().Compile();
LambdaWrapper wrapper = new LambdaWrapper(compiledInnerLambda);
lambdaBody.Add(Expression.Constant(wrapper));
//lambdaBody.Add(GetInnerLambda());
return Expression.Lambda(
Expression.Block(
new ParameterExpression[] { Param },
lambdaBody));
}
public LambdaExpression GetInnerLambda() {
return Expression.Lambda(
Expression.Block(
Expression.Call(null,
typeof(ForSO).GetMethod("Write"),
Expression.Constant("Inner lambda start")),
Expression.Call(null,
typeof(ForSO).GetMethod("Write"),
Param),
Expression.Call(null,
typeof(ForSO).GetMethod("Write"),
Expression.Constant("Inner lambda end"))
)
);
}
public static void Write(object toWrite) {
Console.WriteLine(toWrite);
}
public static void Main(string[] args) {
ForSO so = new ForSO();
LambdaWrapper wrapper = so.GetOuterLambda().Compile()
.DynamicInvoke() as LambdaWrapper;
wrapper.Execute();
//(so.GetOuterLambda().Compile().DynamicInvoke() as Delegate).DynamicInvoke();
}
}
}
Il problema è che in linea di GetInnerLambda().Compile()
in GetOuterLambda
metodo. Sono a conoscenza di una soluzione: è nella parte del codice commentata. Detto questo, tutto funziona bene, ma ho bisogno di un wrapper come valore di ritorno, non di sottoalbero di espressione (potrebbe essere ok per memorizzare sottostruttura lambda interna in LambdaWrapper e compilarlo in seguito, ma lo stesso problema si verifica).
L'errore che ottengo è Unhandled Exception: System.InvalidOperationException: variable 'Param' of type 'System.Object' referenced from scope '', but it is not defined
.
Se aggiungo Param
per bloccare le variabili nel lambda interno, il codice viene compilato, ma Param non ha valore assegnato in lambda esterno (e questo ha senso).
Come può essere risolto?
Grazie per la risposta.Quello che non mi piace di questo approccio è che quei lambda sono funzioni reali, e possono avere i loro parametri (non ho incluso quella parte in questione perché non ho un problema con quello), e Param non è solo variabile ho bisogno di accedere (ci possono essere molti di loro), quindi non credo che aggiungere parametri artificiali per risolvere l'ambito sia una soluzione molto elegante. –
La risposta aggiornata potrebbe funzionare solo per me, ma dovrò controllare quando torno al mio computer funzionante. Grazie ... –
Ho controllato se questo funziona per me. Quasi :). Ho fatto un ulteriore passo avanti e ho creato DynamicExpression per creare un'istanza di LambdaWrapper. Dovevo creare leganti, quindi quest'anima richiede più lavoro, ma li avevo già nel mio progetto principale. Grazie per l'interesse dimostrato nel risolvere questo problema :) –