У меня есть дерево выражений, которое я создал путем анализа Xml с использованием класса выражений в C#. Посмотреть этот вопрос.
У меня есть только Сложение, Вычитание, Разделение, Умножение, Параметры, И и Или в моем Дереве выражений. Есть ли способ преобразовать это ExpressionTree в вызываемый метод? ... или мне нужно вручную испускать IL?
С уважением,





Вам нужно создать лямбду - т.е.
var lambda = Expression.Lambda<Func<float,int>>(body, param);
Func<float,int> method = lambda.Compile();
int v = method(1.0); // test
где "body" - ваше дерево выражений (принимающее число с плавающей запятой, возвращающее целое число), включающее параметр ParameterExpression.
Вы также можете найти это и это полезными.
Возможны несколько параметров (это массив params из ParameterExpression); для подвыражений вам нужно вызвать внутреннее выражение (Expression.Invoke?)
Вы также можете построить все дерево в одном выражении; это немного эффективнее, но сложнее.
Изначально я пытался собрать все дерево в одно выражение. Тогда не мог придумать способ добраться до параметров в подвыражениях. Любая информация по этому поводу поможет.
Если у вас есть одно выражение, вы можете использовать столько экземпляров ParameterExpression, сколько вам нужно. Для подвыражений вам просто нужно сопоставить вещи в самом внешнем выражении с параметрами в выражении внутренний. Например, вам может потребоваться создать новое выражение ParameterExpression для внешнего ...
... и просто передайте это в параметры, установленные при использовании Expression.Invoke - затем это отображается как значение во внутреннее выражение (которое также должно быть LambdaExpression через Expression.Lambda)
Если у вас есть конкретный пример, я могу заполнить некоторые пробелы - но мне нужно оторваться на несколько часов ... вернемся немного ;-p
Вот пример обоих подходов. Если я что-то пропустил или вам нужна дополнительная информация, просто дайте мне знать.
static void Main()
{
// try to do "x + (3 * x)"
var single = BuildSingle<decimal>();
var composite = BuildComposite<decimal>();
Console.WriteLine("{0} vs {1}", single(13.2M), composite(13.2M));
}
// utility method to get the 3 as the correct type, since there is not always a "int x T"
static Expression ConvertConstant<TSource, TDestination>(TSource value)
{
return Expression.Convert(Expression.Constant(value, typeof(TSource)), typeof(TDestination));
}
// option 1: a single expression tree; this is the most efficient
static Func<T,T> BuildSingle<T>()
{
var param = Expression.Parameter(typeof(T), "x");
Expression body = Expression.Add(param, Expression.Multiply(
ConvertConstant<int, T>(3), param));
var lambda = Expression.Lambda<Func<T, T>>(body, param);
return lambda.Compile();
}
// option 2: nested expression trees:
static Func<T, T> BuildComposite<T>()
{
// step 1: do the multiply:
var paramInner = Expression.Parameter(typeof(T), "inner");
Expression bodyInner = Expression.Multiply(
ConvertConstant<int, T>(3), paramInner);
var lambdaInner = Expression.Lambda(bodyInner, paramInner);
// step 2: do the add, invoking the existing tree
var paramOuter = Expression.Parameter(typeof(T), "outer");
Expression bodyOuter = Expression.Add(paramOuter, Expression.Invoke(lambdaInner, paramOuter));
var lambdaOuter = Expression.Lambda<Func<T, T>>(bodyOuter, paramOuter);
return lambdaOuter.Compile();
}
Лично я бы выбрал первый метод; это и проще, и эффективнее. Это может включать передачу исходного параметра через стек вложенного кода, но пусть будет так. У меня где-то есть код, который использует подход "Invoke" (составной) и переписывает дерево как первый подход (одиночный), но он довольно сложный и длинный. Но очень полезно для Entity Framework (который не поддерживает Expression.Invoke).
Проблема заключается в следующем: ((x + 2) + y) / z Что мне делать, если в дереве есть несколько частей параметров различных подвыражений?