Как мне скомпилировать дерево выражений в вызываемый метод C#?

У меня есть дерево выражений, которое я создал путем анализа Xml с использованием класса выражений в C#. Посмотреть этот вопрос.

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

С уважением,

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
12
0
6 440
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вам нужно создать лямбду - т.е.

var lambda = Expression.Lambda<Func<float,int>>(body, param);
Func<float,int> method = lambda.Compile();
int v = method(1.0); // test

где "body" - ваше дерево выражений (принимающее число с плавающей запятой, возвращающее целое число), включающее параметр ParameterExpression.

Вы также можете найти это и это полезными.

Проблема заключается в следующем: ((x + 2) + y) / z Что мне делать, если в дереве есть несколько частей параметров различных подвыражений?

SharePoint Newbie 06.12.2008 20:56

Возможны несколько параметров (это массив params из ParameterExpression); для подвыражений вам нужно вызвать внутреннее выражение (Expression.Invoke?)

Marc Gravell 06.12.2008 20:58

Вы также можете построить все дерево в одном выражении; это немного эффективнее, но сложнее.

Marc Gravell 06.12.2008 21:01

Изначально я пытался собрать все дерево в одно выражение. Тогда не мог придумать способ добраться до параметров в подвыражениях. Любая информация по этому поводу поможет.

SharePoint Newbie 06.12.2008 21:03

Если у вас есть одно выражение, вы можете использовать столько экземпляров ParameterExpression, сколько вам нужно. Для подвыражений вам просто нужно сопоставить вещи в самом внешнем выражении с параметрами в выражении внутренний. Например, вам может потребоваться создать новое выражение ParameterExpression для внешнего ...

Marc Gravell 06.12.2008 21:05

... и просто передайте это в параметры, установленные при использовании Expression.Invoke - затем это отображается как значение во внутреннее выражение (которое также должно быть LambdaExpression через Expression.Lambda)

Marc Gravell 06.12.2008 21:06

Если у вас есть конкретный пример, я могу заполнить некоторые пробелы - но мне нужно оторваться на несколько часов ... вернемся немного ;-p

Marc Gravell 06.12.2008 21:06
Ответ принят как подходящий

Вот пример обоих подходов. Если я что-то пропустил или вам нужна дополнительная информация, просто дайте мне знать.

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).

Другие вопросы по теме