Я создаю дерево выражений, и возникает ситуация, когда мне нужно создать одну лямбду в другой лямбде, сохранить внутреннюю в классе и добавить этот класс в дерево выражений. Это простой пример того, что я пытаюсь сделать (этот код не компилируется):
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();
}
}
}
Проблема в строке GetInnerLambda().Compile()
в методе GetOuterLambda
.
Я знаю одно решение - оно находится в закомментированной части кода. При этом все работает нормально, но мне нужна оболочка в качестве возвращаемого значения, а не поддерево выражения (может быть, можно сохранить внутреннее поддерево лямбда в LambdaWrapper и скомпилировать его позже, но возникает та же проблема).
Ошибка, которую я получаю: Необработанное исключение: System.InvalidOperationException: переменная 'Param' типа 'System.Object' указана из области видимости '', но она не определена
.
Если я добавлю Param
к блочным переменным во внутренней лямбде, код компилируется, но Param не имеет значения, назначенного во внешней лямбде (и это имеет смысл).
Как это можно решить?