2013-06-17 2 views
1

Это все о методе CompileExpression Тип. Извините, что я наивный, так как я поздний. Я читал о выражении здания, чтобы обеспечить динамическую модификацию исполняемого кода. И это имеет смысл для меня, когда речь заходит об испускании лямбда-выражения из данного дерева выражений, если для изменяющихся входов/среды (скажем, для разных значений для любого заданного выражения константы/параметра/члена). Я полагаю, было бы идеально, если бы я мог кэшировать (повторно использовать) лямбда, которые генерируются/скомпилированы из дерева выражений при условии, что в среде нет изменений.Дерево выражений и метод компиляции

Вопрос: Является ли CLR всегда излучать лямбда-выражение, даже если у меня нет изменений в окружающей среде? Если да, то что лучше всего было избежать компиляции выражения из лямбда, если нет изменений в среде?

+1

Я думаю, это зависит от того, как именно вы используете деревья выражений. Не могли бы вы добавить конкретный пример того, как вы можете их использовать? – svick

ответ

2

Лямбда-выражения - это всего лишь способ представить кусок кода: назовите это, вызовите это, сравните эти аргументы, верните что-нибудь и т. Д. Почти так же, как вы делаете это из редактора кода, или JIT делает из IL.

Compile испускает делегат из определенного выражения лямбда. После того как вы скомпилировали лямбда в делегат, делегат остается неизменным (лямбда остается неизменной, потому что она неизменна).

Это не означает, что делегат не может принимать какие-либо аргументы или вызывать какой-либо метод любого объекта. Это означает, что IL-код делегата не изменяется. И да, вы можете кэшировать скомпилированный экземпляр делегата.

2

CLR не кэширует лямбда-выражения, Compile() каждый раз возвращает новый делегат.

Но это должно быть легко кэша, через что-то вроде этого:

public Func<T> Get<T>(Expression<Func<T>> expression) 
{ 
    string key = expression.Body.ToString(); 

    Func<T> result; 
    if (!_cache.TryGetValue(key, out result)) { 
     result = expression.Compile(); 
     _cache.Add(key, result); 
    } 

    return result; 
} 
+1

Я думаю, использование 'ToString()' для сравнения равенства является хрупким. Например, вы можете создать два 'выражения ', оба из которых будут иметь строковое представление' (x, x) => (x + x) ', но будут возвращать разные результаты. Хотя альтернатива (правильно структурно сравнивающая выражение) не является тривиальной. – svick

+0

Кроме того, ваш код на самом деле не работает, потому что вы не можете иметь общие поля ('_cache' должен быть' Dictionary > ', что невозможно). Но это может быть легко зафиксировано броском. – svick

+0

Да, это скорее своего рода псевдокод, согласованный – andreister

0

Вызов Compile() будет возвращать новый делегат каждый раз, и каждый раз, когда новый код MSIL излучается. Это медленно и эффективно создает утечку памяти , так как код MSIL не подлежит сбору мусора. Я создал библиотеку, которая предлагает кэшированную компиляцию, которая фактически правильно сравнивает структуру выражений и позволяет повторно использовать кэшированных делегатов. Все константы и замыкания автоматически заменяются параметрами и вставляются в делегат во внешнем закрытии. Это позволяет избежать утечки памяти и намного быстрее. Проверьте это здесь: https://github.com/Miaplaza/expression-utils