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

问题描述 投票:0回答:2

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

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
变量(在表达式/闭包内)时出现空引用异常。

我在方法中找不到任何提升的(?)局部变量,所以我想知道它们是否被捕获?

c# lambda closures
2个回答
19
投票

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

看起来您正在通过“手动”调用工厂方法来自己构建表达式树 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 表达式内。如果您使用工厂,编译器会假设您知道自己在做什么,并且不会乱七八糟地重写它。你必须自己把它吊起来。


2
投票

我认为您正在寻找

Expression.Quote
,它支持 Lambda 表达式中的变量捕获。基本上,内部
LambdaExpression
(将引用捕获的变量)需要包装在
Expression.Quote(...)
调用中。

此处的示例和讨论:Expression.Quote() 能做什么而 Expression.Constant() 不能做什么?

© www.soinside.com 2019 - 2024. All rights reserved.