Ho un metodo metodo
private static int Method(int n)
{
return n;
}
Ottengo MethodCallExpression in VisitMethodCall sovrascritto nel mio ExpressionVisitor. MethodCallExpression contiene:
n => Method(2 + n)
Voglio compilarlo in Func e chiamare così:
func(3)
Ed è necessario restituire 5.
Ho provato questo:
IEnumerable<ParameterExpression> parameters = expression.Arguments.Select(a => Expression.Parameter(a.Type, a.ToString()));
MethodCallExpression call = Expression.Call(expression.Method, parameters);
Expression<Func<Int32, Int32>> lambda = Expression.Lambda<Func<int, int>>(call, call.Arguments.OfType<ParameterExpression>());
var func = lambda.Compile();
Console.WriteLine(func(3));
E mi restituisce 3, non 5
È perché 2 + x è il nome param e lo sostituisco con 3.
Non sei sicuro del motivo per cui lo faresti mai, ma comunque per farlo devi estrarre parametri (non argomenti) usati da MethodCallExpression
. Per questo puoi abusare del visitatore di espressioni in questo modo:
public class ParametersExtractorVisitor : ExpressionVisitor {
public IList<ParameterExpression> ExtractedParameters { get; } = new List<ParameterExpression>();
protected override Expression VisitParameter(ParameterExpression node) {
ExtractedParameters.Add(node);
return base.VisitParameter(node);
}
}
Quindi usalo in questo modo nel tuo codice:
var visitor = new ParametersExtractorVisitor();
visitor.Visit(expression);
MethodCallExpression call = Expression.Call(expression.Method, expression.Arguments);
Expression<Func<Int32, Int32>> lambda = Expression.Lambda<Func<int, int>>(call, visitor.ExtractedParameters);
var func = lambda.Compile();
Console.WriteLine(func(3));
Non hai bisogno di un visitatore per implementarlo.
Fondamentalmente la tua funzione Method
dovrebbe essere fornita con il risultato di un'operazione di addizione del valore passato alla lambda con il valore costante di 2.
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Test
{
class Program
{
static void Main(string[] args) {
var method = typeof(Program).GetMethod("Method", BindingFlags.Static | BindingFlags.Public);
var parameter = Expression.Parameter(typeof(int), "n");
var add = Expression.Add(Expression.Constant(2, typeof(int)), parameter);
var methodCallExpression = Expression.Call(null, method, add);
var lambda = Expression.Lambda<Func<int, int>>(methodCallExpression, parameter);
var func = lambda.Compile();
Console.WriteLine(func(3));
}
public static int Method(int n) => n;
}
}
Lo implementerei usando il visitatore mutando MethodCallExpression
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace Test
{
class MethodCallVisitor : ExpressionVisitor
{
private readonly int toAdd;
public MethodCallVisitor(int toAdd) {
this.toAdd = toAdd;
}
protected override Expression VisitMethodCall(MethodCallExpression node) {
var add = Expression.Add(node.Arguments.First(), Expression.Constant(toAdd));
return Expression.Call(node.Object, node.Method, add);
}
}
class Program
{
static void Main(string[] args) {
var methodCallVisitor = new MethodCallVisitor(2);
var method = typeof(Program).GetMethod("Method", BindingFlags.Static | BindingFlags.Public);
var parameter = Expression.Parameter(typeof(int), "n");
var methodCallExpression = Expression.Call(null, method, parameter);
var lambda = Expression.Lambda<Func<int, int>>(methodCallExpression, parameter);
lambda = (Expression<Func<int, int>>)methodCallVisitor.Visit(lambda);
var func = lambda.Compile();
Console.WriteLine(func(3));
}
public static int Method(int n) => n;
}
}