Дерево выражений — компилировать внутреннюю лямбду во внешнюю лямбду — разрешение области видимости

Я создаю дерево выражений, и возникает ситуация, когда мне нужно создать одну лямбду в другой лямбде, сохранить внутреннюю в классе и добавить этот класс в дерево выражений. Это простой пример того, что я пытаюсь сделать (этот код не компилируется):

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 не имеет значения, назначенного во внешней лямбде (и это имеет смысл).

Как это можно решить?

6
задан del-boy 19 May 2012 в 21:28
поделиться