I am creating expression tree and there is a situation where I need to create one lambda in another lambda and store inner one in a class and add that class in expression tree. This is simple example of what I am trying to do (this code does not compile):
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();
}
}
}
Problem is in GetInnerLambda().Compile()
line in GetOuterLambda
method.
I am aware of one solution - it is in commented part of code. With that, everything works fine, but I need a wrapper as return value, not expression subtree (it might be ok to store inner lambda subtree in LambdaWrapper, and compile it later, but same problem occures).
Error I am getting is Unhandled Exception: System.InvalidOperationException: variable 'Param' of type 'System.Object' referenced from scope '', but it is not defined
.
If I add Param
to block variables in inner lambda, code compiles, but Param has not value assigned in outer lambda (and that makes sense).
How can this be solved?
With help from Balazs Tihanyi I found solution that works for me exactly as I need it. It is a bit more work because I had to create binders, but I my main project I already had them, so I created dummy binders for this example to work.
This is my final solution: