Таким образом, у меня есть 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
бит.
Так, как я могу сделать дерево выражений, которое вызывает делегата?
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
слишком сложная функция. По этой причине я не считаю этот ответ хорошим, но может быть он может быть построен на основе?
Я думаю, что вы хотите использовать свойства 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
Это должно сработать:
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
Единственный Другой способ, о котором я могу думать, - это захват делегата, объявленного локально в закрытии, но я бы не знал, как это сделать.