表达式树和编译方法

时间:2013-06-17 15:12:59

标签: c# lambda expression-trees

这完全与Compile类型的Expression方法有关。抱歉天真,因为我是后来者。我一直在阅读有关构建表达式的信息,以便动态修改可执行代码。对于我来说,只要对于不同的输入/环境(例如对于任何给定的常量/参数/成员表达式的不同值),从给定的表达式树中发出lambda表达式对我来说是有意义的。我认为,如果我可以缓存(重复使用)lambda那些是从表达式树生成/编译的,只要环境没有变化,那将是理想的。

问题:即使环境没有变化,CLR是否总是发出lambda表达式?如果是这样,那么如果环境没有变化,最好避免从lambda编译表达式?

3 个答案:

答案 0 :(得分:3)

每次返回新的委托时,CLR都不会缓存lambda表达式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 :(得分:2)

Lambda表达式只是表示一段代码的一种方式:调用它,调用它,比较这些参数,返回一些东西等。几乎与从代码编辑器执行它的方式相同,或者JIT从IL执行它。

Compile从特定的lambda表达式发出委托。一旦你将lambda编译成委托,代理保持不变(lambda也保持不变,因为它是不可变的)。

这并不意味着,该委托不能接受任何参数或调用任何对象的任何方法。这只是意味着,代表的IL不会改变。是的,您可以缓存已编译的委托实例。

答案 2 :(得分:0)

每次调用Compile()都会返回一个新的委托,每次都会发出新的MSIL代码。这很慢并且有效地创建了内存泄漏,因为MSIL代码不受垃圾回收的影响。我创建了一个提供缓存编译的库,它实际上正确地比较了表达式的结构,并允许重用缓存的委托。所有常量和闭包都会自动替换为参数,并在外部闭包中重新插入到委托中。这可以避免泄漏内存并且速度更快。请在此处查看:https://github.com/Miaplaza/expression-utils