Как я могу сгенерировать вложенное лямбда-выражение с захватом переменных - PullRequest
0 голосов
/ 17 января 2019

Я пытаюсь сгенерировать выражение типа (Foo foo) => () => foo.Bar(), а затем запустить внешнюю лямбду, предоставив экземпляр Foo, чтобы возвращаемое значение было замыканием, которое статически вызывает Foo.Bar, чтобы избежать неявного перехвата исключений, введенного динамический вызов.

Однако вызов внешнего выражения завершается неудачно с исключением «переменная 'foo' типа 'ConsoleApp2.Foo', на которую ссылается область действия '', но она не определена".

Я совершаю какую-то ошибку или есть какая-то концептуальная причина, предотвращающая ее?

Минимальный полный код:

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");
        }
    }
}

Ответы [ 2 ]

0 голосов
/ 17 января 2019

Я думаю, это потому, что вам нужно ссылаться на одну и ту же переменную, это, похоже, решит вашу проблему.

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

var expr = Expression.Lambda(
    Expression.Lambda(
        Expression.Call(foo, typeof(Foo).GetMethod("Bar"))), 
            new[] { foo });
0 голосов
/ 17 января 2019

Необходимо убедиться, что выражения переменных в лямбда-выражении ссылаются на один и тот же экземпляр.

Этот код работает просто отлично и, похоже, дает ожидаемый результат.

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");
    }
}
...