Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Issue with closure variable capture in c# expression

I have a function which creates a delegate using expression trees. Within this expression I use a variable captured from multiple parameters passed in to the function. The actual expression tree is rather large so as an example:

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 then invoke this method from another method before passing that function to another function to use. Once that's all done I want to access the content of parents which gets updated within the expression tree.

Everything seems to compile, and my expression looks ok, but when I run it I appear (although I can't really be sure) to be getting null reference exceptions when accessing the parents variable (inside the expression/closure).

I guess I'd like to know if I'm doing something wrong or whether this is possible as well as tips for understanding what's going on. 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?

Thanks, Mark

like image 470
Mark Jerzykowski Avatar asked Jan 29 '13 14:01

Mark Jerzykowski


People also ask

Does C support closure?

Although C was created two decades after Lisp, it nonetheless lacks support for closures.

Why closures are reference types?

Closures Are Reference TypesWhenever you assign a function or a closure to a constant or a variable, you are actually setting that constant or variable to be a reference to the function or closure.

What are closures in C?

A closure is a kind of object that contains a pointer or reference of some kind to a function to be executed along with the an instance of the data needed by the function.

What is a captured variable?

A captured variable is one from the outside of your local class - one declared in the surrounding block. In some languages this is called a closure.


1 Answers

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?

It looks like you are building the expression tree lambda yourself, by "manually" calling the factory methods. The compiler has no idea that that's what you're doing; it just sees method calls. If you want locals to be hoisted then you're going to have to either (1) get the compiler to do it for you, by making it rewrite the lambda, or (2) hoist 'em yourself.

That is:

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

the compiler rewrites the lambda and hoists it for you, as though you'd said:

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

Where c typically becomes a Constant expression.

But if you say

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

the compiler has no idea that you're doing something where it needs to hoist x; x is not inside a lambda expression. If you're using the factories, the compiler assumes you know what you're doing and doesn't mess around with rewriting it. You'll have to hoist it yourself.

like image 150
Eric Lippert Avatar answered Oct 29 '22 02:10

Eric Lippert