Компиляция и выполнение выражений
Преобразуйте деревья выражений в исполняемые делегаты.
«Компиляция и выполнение выражений» — бесплатный урок C# Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C# Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C# Academy содержит 4 уроков всего.
От дерева к делегату
Дерево выражений остаётся данными, пока Вы не скомпилируете его. Expression<TDelegate>.Compile() превращает дерево в настоящий делегат, который можно вызвать.
using System;
using System.Linq.Expressions;
Expression<Func<int, int>> tree = x => x + 1;
Func<int, int> fn = tree.Compile();
Console.WriteLine(fn(5)); // 6Компиляция построенного вручную дерева
Деревья, созданные вручную, компилируются таким же образом и порождают работающий делегат.
using System;
using System.Linq.Expressions;
var x = Expression.Parameter(typeof(int), "x");
var body = Expression.Multiply(x, Expression.Constant(3));
var lambda = Expression.Lambda<Func<int, int>>(body, x);
var fn = lambda.Compile();
Console.WriteLine(fn(4)); // 12Вызов результата
Скомпилированный делегат ведёт себя как любой другой: его можно вызвать, сохранить и передать.
using System;
using System.Linq.Expressions;
Expression<Func<int, int, int>> addExpr = (a, b) => a + b;
var add = addExpr.Compile();
Console.WriteLine(add(2, 3)); // 5
Console.WriteLine(add(10, 20)); // 30Compile не бесплатен
Компиляция требует ресурсов во время выполнения, поэтому кэшируйте полученный делегат и используйте его повторно вместо компиляции при каждом вызове.
using System;
using System.Linq.Expressions;
Expression<Func<int, int>> tree = x => x * x;
var square = tree.Compile(); // compile once
for (int i = 1; i <= 3; i++)
Console.Write(square(i) + " "); // 1 4 9
Console.WriteLine();Динамическое построение предикатов
Один из распространённых вариантов использования — построение предикатов фильтрации во время выполнения на основе пользовательского ввода с последующей компиляцией в Func.
using System;
using System.Linq.Expressions;
var n = Expression.Parameter(typeof(int), "n");
var isPositive = Expression.GreaterThan(n, Expression.Constant(0));
var pred = Expression.Lambda<Func<int, bool>>(isPositive, n).Compile();
Console.WriteLine(pred(5)); // True
Console.WriteLine(pred(-2)); // FalseОбъединение условий
Используйте Expression.AndAlso / OrElse, чтобы объединить предикаты перед компиляцией.
using System;
using System.Linq.Expressions;
var n = Expression.Parameter(typeof(int), "n");
var inRange = Expression.AndAlso(
Expression.GreaterThanOrEqual(n, Expression.Constant(1)),
Expression.LessThanOrEqual(n, Expression.Constant(10)));
var fn = Expression.Lambda<Func<int, bool>>(inRange, n).Compile();
Console.WriteLine(fn(5) + " " + fn(11)); // True FalseКомпиляция вычислений
Вы можете собирать арифметические формулы во время выполнения, что полезно для механизмов правил и калькуляторов.
using System;
using System.Linq.Expressions;
var p = Expression.Parameter(typeof(double), "p");
// price * 1.2 (add 20% tax)
var withTax = Expression.Multiply(p, Expression.Constant(1.2));
var calc = Expression.Lambda<Func<double, double>>(withTax, p).Compile();
Console.WriteLine(calc(100)); // 120DynamicInvoke для нетипизированных лямбд
Если Вы использовали необобщённый Expression.Lambda, скомпилируйте его в Delegate и вызовите DynamicInvoke.
using System;
using System.Linq.Expressions;
var x = Expression.Parameter(typeof(int), "x");
var body = Expression.Add(x, Expression.Constant(10));
Delegate d = Expression.Lambda(body, x).Compile();
Console.WriteLine(d.DynamicInvoke(5)); // 15Примечание о производительности
После однократной компиляции скомпилированное выражение выполняется примерно с той же скоростью, что и делегат. Для часто выполняемых участков скомпилируйте его один раз при запуске и сохраните делегат.
using System;
using System.Linq.Expressions;
Expression<Func<int, int>> tree = x => x + x;
var doubler = tree.Compile();
int total = 0;
for (int i = 0; i < 5; i++) total += doubler(i);
Console.WriteLine(total); // 0+2+4+6+8 = 20Ошибки обнаруживаются во время компиляции
Если дерево составлено неправильно (например, отсутствует параметр), вызов Compile() вызывает исключение, поэтому проверяйте деревья перед их использованием.
using System;
using System.Linq.Expressions;
var x = Expression.Parameter(typeof(int), "x");
var y = Expression.Parameter(typeof(int), "y");
try {
// body references y but lambda only declares x
var lambda = Expression.Lambda<Func<int, int>>(Expression.Add(x, y), x);
lambda.Compile();
} catch (InvalidOperationException) {
Console.WriteLine("unbound parameter");
}Объединение всех элементов
Постройте, скомпилируйте и выполните небольшую формулу в рамках одного процесса.
using System;
using System.Linq.Expressions;
var x = Expression.Parameter(typeof(int), "x");
var formula = Expression.Add(
Expression.Multiply(x, Expression.Constant(2)),
Expression.Constant(3)); // 2x + 3
var fn = Expression.Lambda<Func<int, int>>(formula, x).Compile();
Console.WriteLine(fn(4)); // 11Быстрая проверка
Убедитесь, что понимаете, как выполнить дерево выражений.
Повторение
Вы научились компилировать и выполнять деревья выражений.
Compile()превращает дерево в вызываемый делегат.- Кэшируйте делегат: компиляция требует ресурсов только один раз.
- Динамически создавайте предикаты и формулы, а затем компилируйте их.
- Неправильно построенные деревья вызывают исключение при вызове
Compile().
Часто задаваемые вопросы
Урок «Компиляция и выполнение выражений» бесплатный?
Да — полный текст урока «Компиляция и выполнение выражений» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C# Academy, подпишись на CoddyKit PRO. Курс C# Academy содержит 4 уроков всего.
Чему я научусь в уроке «Компиляция и выполнение выражений»?
Преобразуйте деревья выражений в исполняемые делегаты. Ты практикуешь C# Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать C# Academy?
Предыдущий опыт не требуется. C# Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Компиляция и выполнение выражений»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке C# Academy?
Да. Каждый урок C# Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Что такое деревья выражений
- Построение выражений вручную
- Компиляция и выполнение выражений
- Деревья выражений в поставщиках LINQ