c#表达式中的闭包变量捕获问题
我有一个使用表达式树创建委托的函数。 在这个表达式中,我使用从传入函数的多个参数中捕获的变量。 实际的表达式树相当大,例如:
Delegate GenerateFunction(T current, IList parents) { var currentExpr = Expression.Parameter(typeof(T), "current"); var parentsExpr = Expression.Parameter(parents.getType(), "parents"); var parameters = new List(); .... return Expression.Lambda(Expression.Block(new List { parentsExpr, currentExpr }, ....), parameters.ToArray()).Compile(); }
然后,我在将该函数传递给另一个要使用的函数之前,从另一个方法调用此方法。 完成所有操作后,我想访问在表达式树中更新的父项内容。
一切似乎都在编译,我的表达式看起来还不错,但是当我运行它时,我出现(虽然我不能确定)在访问parent变量时(在表达式/闭包内)获取空引用exception。
我想我想知道我做错了什么,或者这是否可行,以及了解发生了什么的提示。 我似乎无法在方法中找到任何悬挂的(?)局部变量,所以我想知道它们是否被捕获了?
谢谢,马克
我似乎无法在方法中找到任何悬挂的局部变量,所以我想知道它们是否被捕获了?
看起来您正在通过“手动”调用工厂方法来自己构建表达式树lambda。 编译器不知道你正在做什么; 它只是看到方法调用。 如果你想要提升当地人,那么你将不得不(1)让编译器为你做这件事, 让它重写lambda,或者(2)自己提升他们。
那是:
int x = 123; Expression> ex = ()=>x;
编译器重写lambda并为你提升它,就像你说的那样:
Closure c = new Closure(); cx = 123; Expression> ex = ()=>cx;
其中c
通常变为常量表达式。
但如果你说
Expression> ex = Expression.Lambda( ...something that uses x ... );
编译器不知道你正在做一些需要提升x的东西; x不在lambda表达式中。 如果您正在使用工厂,编译器会假设您知道自己在做什么,并且不会重写它。 你必须自己提升它。
我想你正在寻找Expression.Quote
,它支持Lambda表达式中的变量捕获。 基本上,内部LambdaExpression
(将引用捕获的变量)需要包含在Expression.Quote(...)
调用中。
这里的示例和讨论: Expression.Quote()做了什么,Expression.Constant()不能做什么?