Можно ли создать дерево выражений для динамических операторов if? - PullRequest
7 голосов
/ 23 марта 2012

У меня есть ситуация, когда я читаю бизнес-логику и заменяю переменные фактическими значениями, а затем мне нужно оценить ее, чтобы получить результат. В настоящее время я использую bcParser для этого, и он прекрасно работает для всей логики, которая написана в формате Excel.

Кривой мяч, брошенный в меня, заключается в том, что если условие не будет похоже на excel if(cond, true, false), скорее оно будет похоже на C #, где if (cond) { true; } else { false;}, это имеет больше смысла и его легко поддерживать. Так как я заменяю все переменные значением перед этим, все, что мне нужно сделать, это оценить его. В настоящее время я решаю эту проблему, экспортируя логику в методы c # и используя рефлексию. Я оцениваю ее, и она также работает.

Мне интересно, есть ли другой вариант, я не хочу писать код для каждого условия if и хотел бы оценить его во время выполнения. Мне было интересно, смогу ли я создать какой-нибудь синтаксический анализатор токенов, вызвать оценку собственного выражения C # и выполнить вычисление. Я не разбирался в деревьях выражений, кажется, это возможно при таком подходе. прежде чем я пойду туда, я хотел бы знать, возможно ли это вообще? Спасибо,

1 Ответ

5 голосов
/ 24 марта 2012

Да!

Ключ использует пространство имен System.Linq.Expressions.Вы можете создать дерево выражений программно, либо в своем коде, либо изменив свой синтаксический анализатор, а затем скомпилировать его в Delegate.Этот API компилирует ваш Delegate внутри DynamicAssembly, что означает, что ваши скомпилированные выражения могут быть выгружены из памяти сборщиком мусора, когда вы полностью разыменуете их.

Вот очень простой пример:

var b = true;
Func<bool> condition = () => b;
Action trueExpression = () => { Console.WriteLine(true); };
Action falseExpression = () => { Console.WriteLine(false); };

var e = Expression.Condition(
    Expression.Invoke(Expression.Constant(condition)),
    Expression.Invoke(Expression.Constant(trueExpression)),
    Expression.Invoke(Expression.Constant(falseExpression)));

var λ = Expression.Lambda(e).Compile();

b = true;
λ.DynamicInvoke();

b = false;
λ.DynamicInvoke();

Это приводит к выводу:

True
False

Шаг, где выражение компилируется в лямбду, может быть значительнымснижение производительности, вы захотите придумать стратегию кэширования для ваших скомпилированных лямбд.Хотя это того стоит, вызов скомпилированной лямбды с помощью DynamicInvoke очень быстрый.Почти так же быстро, как если бы вы предварительно скомпилировали его.Этот метод значительно быстрее, чем использование генерации кода CodeDom (для компиляции которого требуется совсем другой процесс), и он имеет основное преимущество в создании выгружаемых сборок.

Единственное ограничение в том, что вы не можете создавать типыс этим API.Вы должны ограничить себя выражениями и заявлениями.Это довольно мощно, однако, это волшебные силы DLR.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...