Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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");
        }
    }
}
like image 775
max630 Avatar asked Sep 17 '25 04:09

max630


1 Answers

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");
    }
}
like image 174
Rodrick Chapman Avatar answered Sep 19 '25 09:09

Rodrick Chapman