max630
max630

Reputation: 9248

How could I generate nested lambda expression with capturing variables

I am trying to generate expression like (Foo foo) => () => foo.Bar(), and then run the outer lambda providing the Foo instance, so that returned value would be a closure which statically calls Foo.Bar, to avoid implicit exception catching introduced by the dynamic call.

However, calling the outer expression fails with exception "variable 'foo' of type 'ConsoleApp2.Foo' referenced from scope '', but it is not defined".

Am I making some mistake or there is some conceptual reason preventing it?

The minimal full code:

using System;
using System.Linq.Expressions;

namespace ConsoleApp2 {
    class Program {
        static void Main(string[] args)
        {
            // supposed to be "(Foo foo) => () => foo.Bar()",
            // inspecting `expr` in debugger seems to agree
            var expr = Expression.Lambda(
                Expression.Lambda(
                    Expression.Call(Expression.Variable(typeof(Foo), "foo"), typeof(Foo).GetMethod("Bar"))),
                new[] { Expression.Variable(typeof(Foo), "foo") });

            // here exception "variable 'foo' of type 'ConsoleApp2.Foo' referenced from scope '', but it is not defined" is thrown
            var res = (Action)expr.Compile().DynamicInvoke(new Foo());

            res();
        }
    }

    class Foo
    {
        public void Bar()
        {
            Console.WriteLine("Bar");
        }
    }
}

Upvotes: 4

Views: 363

Answers (2)

Svek
Svek

Reputation: 12878

I would imagine it is because you need to reference the same variable, this seems to resolve your problem.

var foo = Expression.Variable(typeof(Foo), "foo");

var expr = Expression.Lambda(
    Expression.Lambda(
        Expression.Call(foo, typeof(Foo).GetMethod("Bar"))), 
            new[] { foo });

Upvotes: 4

Rodrick Chapman
Rodrick Chapman

Reputation: 5543

You need to make sure that the variable expressions in the lambda expression reference the same instance.

This code runs just fine, and seems to give the expected result.

void Main()
{
   var var = Expression.Variable(typeof(Foo), "foo");

   var expr = Expression.Lambda(
    Expression.Lambda(
        Expression.Call(var, typeof(Foo).GetMethod("Bar"))), new[] {  var });

    var res = (Action)expr.Compile().DynamicInvoke(new Foo());

    res();
}

class Foo
{
    public void Bar()
    {
        Console.WriteLine("Bar");
    }
}

Upvotes: 4

Related Questions