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

Таким образом, у меня есть a 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 бит.

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

18
задан abatishchev 6 April 2017 в 00:20
поделиться

3 ответа

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

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 слишком сложная функция. По этой причине я не считаю этот ответ хорошим, но может быть он может быть построен на основе?

.
10
ответ дан 30 November 2019 в 08:21
поделиться

Я думаю, что вы хотите использовать свойства Target и Method делегата для передать, чтобы создать выражение вызова. Основываясь на образце 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
14
ответ дан 30 November 2019 в 08:21
поделиться

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

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

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

2
ответ дан 30 November 2019 в 08:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: