我有一个使用表达式树创建委托的函数。在此表达式中,我使用从传递到函数的多个参数捕获的变量。实际的表达式树相当大,例如:
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();
}
然后,我从另一个方法调用此方法,然后将该函数传递给另一个函数来使用。完成这一切后,我想访问在表达式树中更新的父级内容。
一切似乎都在编译,我的表达式看起来没问题,但是当我运行它时,我似乎(尽管我不能真正确定)在访问
parents
变量(在表达式/闭包内)时出现空引用异常。
我在方法中找不到任何提升的(?)局部变量,所以我想知道它们是否被捕获?
我似乎无法在方法中找到任何提升的局部变量,所以我想知道它们是否被捕获?
看起来您正在通过“手动”调用工厂方法来自己构建表达式树 lambda。编译器不知道这就是你正在做的事情;它只看到方法调用。如果您希望提升本地变量,那么您必须 (1) 让编译器通过 it 重写 lambda 来为您完成此操作,或者 (2) 自己提升它们。
即:
int x = 123;
Expression<Func<int>> ex = ()=>x;
编译器重写 lambda 并为你提升它,就像你说的:
Closure c = new Closure();
c.x = 123;
Expression<Func<int>> ex = ()=>c.x;
其中
c
通常成为常量表达式。
但是如果你说
Expression<Func<int>> ex = Expression.Lambda( ...something that uses x ... );
编译器不知道你正在做一些需要提升 x 的事情; x 不在 lambda 表达式内。如果您使用工厂,编译器会假设您知道自己在做什么,并且不会乱七八糟地重写它。你必须自己把它吊起来。
我认为您正在寻找
Expression.Quote
,它支持 Lambda 表达式中的变量捕获。基本上,内部 LambdaExpression
(将引用捕获的变量)需要包装在 Expression.Quote(...)
调用中。
此处的示例和讨论:Expression.Quote() 能做什么而 Expression.Constant() 不能做什么?