10

我有一个使用表达式树创建委托的函数。在这个表达式中,我使用从传递给函数的多个参数中捕获的变量。实际的表达式树相当大,例如:

Delegate GenerateFunction<T>(T current, IList<T> parents) {
    var currentExpr = Expression.Parameter(typeof(T), "current");
    var parentsExpr = Expression.Parameter(parents.getType(), "parents");
    var parameters = new List<ParameterExpression>();

    ....

    return Expression.Lambda(Expression.Block(new List<ParameterExpression> { parentsExpr, currentExpr }, ....), parameters.ToArray()).Compile();
}

然后我从另一个方法调用这个方法,然后将该函数传递给另一个函数以使用。完成后,我想访问在表达式树中更新的父母的内容。

一切似乎都可以编译,我的表达式看起来还不错,但是当我运行它时,我似乎(虽然我不能确定)在访问父变量(在表达式/闭包内)时出现空引用异常。

我想我想知道我是否做错了什么或者这是否可能以及了解正在发生的事情的提示。我似乎无法在方法中找到任何提升的(?)局部变量,所以我想知道它们是否被捕获了?

谢谢,马克

4

2 回答 2

19

我似乎无法在该方法中找到任何提升的局部变量,所以我想知道它们是否被捕获了?

看起来您正在通过“手动”调用工厂方法自己构建表达式树 lambda。编译器不知道你在做什么;它只看到方法调用。如果你想提升本地人,那么你将不得不(1)让编译器为你做这件事,让它重写 lambda,或者(2)自己提升他们。

那是:

int x = 123;
Expression<Func<int>> ex = ()=>x; 

编译器重写 lambda 并为您提升它,就好像您说过:

Closure c = new Closure();
c.x = 123;
Expression<Func<int>> ex = ()=>c.x; 

wherec通常成为一个常量表达式。

但如果你说

Expression<Func<int>> ex = Expression.Lambda( ...something that uses x ... );

编译器不知道您正在做一些需要提升 x 的事情;x 不在 lambda 表达式内。如果您使用的是工厂,编译器会假定您知道自己在做什么,并且不会乱七八糟地重写它。你必须自己把它吊起来。

于 2013-01-29T15:27:28.073 回答
0

我认为您正在寻找Expression.Quote,它支持 Lambda 表达式中的变量捕获。基本上内部LambdaExpression(将引用捕获的变量)需要包含在Expression.Quote(...)调用中。

此处的示例和讨论:Expression.Quote() 做了哪些 Expression.Constant() 不能做的事情?

于 2014-02-01T10:11:14.937 回答