Подтвердить что ты не робот

Деревья выражений и вызов делегата

Итак, у меня есть delegate, который указывает на некоторую функцию, о которой я действительно не знаю, когда я впервые создаю объект delegate. Объект будет установлен на некоторую функцию позже.

Затем я также хочу создать дерево выражений, которое вызывает делегат с аргументом (для этого вопроса аргумент может быть 5). Это бит, с которым я борюсь; приведенный ниже код показывает, что я хочу, но он не компилируется.

Func<int, int> func = null;
Expression expr = Expression.Invoke(func, Expression.Constant(5));

В этом примере я мог бы сделать (это практично, так как мне нужно построить деревья выражений во время выполнения):

Func<int, int> func = null;
Expression<Func<int>> expr = () => func(5);

Это означает, что expr станет:

() => Invoke(value(Test.Program+<>c__DisplayClass0).func, 5)

Что, по-видимому, означает, что для использования delegate func мне нужно создать бит value(Test.Program+<>c__DisplayClass0).func.

Итак, как я могу создать дерево выражений, которое вызывает делегат?

4b9b3361

Ответ 1

ОК, это показывает, как это можно сделать (но на мой взгляд это очень нелогично):

Func<int, int> func = null;
Expression<Func<int, int>> bind = (x) => func(x);

Expression expr = Expression.Invoke(bind, Expression.Constant(5));

Expression<Func<int>> lambda = Expression.Lambda<Func<int>>(expr);
Func<int> compiled = lambda.Compile();

Console.WriteLine(expr);

func = x => 3 * x;
Console.WriteLine(compiled());

func = x => 7 * x;
Console.WriteLine(compiled());

Console.Read();

По существу я использую (x) => func(x); для создания функции, которая вызывает то, на что указывает делегат. Но вы можете видеть, что expr слишком сложно. По этой причине я не считаю этот ответ хорошим, но, возможно, на нем можно построить?

Ответ 2

Я думаю, что вы хотите использовать свойства Target и Method для передачи делегата для создания выражения Call. На основе образца JulianR это будет выглядеть так:

Action<int> func = i => Console.WriteLine(i * i);

var callExpr = Expression.Call(Expression.Constant(func.Target), func.Method, Expression.Constant(5));

var lambdaExpr = Expression.Lambda<Action>(callExpr);
var fn = lambdaExpr.Compile();
fn();    //  Prints 25

Ответ 3

Это должно работать:

Action<int> func = i => Console.WriteLine(i * i);

// If func is null like in your example, the GetType() call fails, 
// so give it a body or use typeof if you know the type at compile time
var param = Expression.Parameter(func.GetType());

// Call the Invoke method on the delegate, which is the same as invoking() it
var callExpr = Expression.Call(param, func.GetType().GetMethod("Invoke"), Expression.Constant(5)); 

var lambdaExpr = Expression.Lambda<Action<Action<int>>>(callExpr, param); 

var fn = lambdaExpr.Compile(); // Compile the expression tree so it can be executed 

fn(func); // Prints 25

Выражения могут быть мозгом, но помните: выражения всегда создаются из других выражений. Выражение - это дерево других выражений, которое описывает код. Вы не можете передать фактический делегат, как в своем примере, то, что вам нужно, является выражением этого делегата, говоря, что выражение ожидает параметр типа вашего делегата. Затем вы говорите, что хотите вызвать метод по этому параметру, а именно метод Invoke, с аргументом "5". Все остальное после этого - просто, если вы хотите превратить выражение в исполняемый код, который вы, вероятно, делаете.

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

РЕДАКТИРОВАТЬ В ответ на комментарий PythonPower:

Я думаю, что то, что вы хотите (не передавая делегату в качестве аргумента), может быть выполнено только тогда, когда сам делегат описывается как выражение:

 var arg = Expression.Parameter(typeof(int), "i");

 var multiply = Expression.Multiply(arg, arg);

 var writeln = Expression.Call(typeof(Console).GetMethod("WriteLine", 
   new[] { typeof(int) }), multiply);

 var lambda = Expression.Lambda<Action<int>>(writeln, arg);

 var compiled = lambda.Compile();

 compiled(5); // Prints 25

Единственный другой способ, о котором я могу думать, - захватить делегат, объявленный локально в закрытии, но я не знаю, как это сделать.