C# · 12月 26, 2021

在c#表达式中关闭变量捕获的问题

我有一个使用表达式树创建一个委托的函数.在这个表达式中,我使用从传入函数的多个参数捕获的变量.实际的表达式树是相当大的例子: 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();}

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

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

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

谢谢,
标记

解决方法

I don’t seem to be able to find any hoisted local variables within the method so I’m wondering whether they’re being captured at all?

看起来你正在自己构建表达式树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;

其中c通常变为Constant表达式.

但如果你说

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

编译器不知道你在做什么,需要提升x; x不在lambda表达式内.如果您正在使用工厂,编译器会假设您知道您正在做什么,并且不会改变它.你必须自己提升.