C# 表达式树与编译方法

C# 表达式树与编译方法,c#,lambda,expression-trees,C#,Lambda,Expression Trees,这是关于Expression类型的Compile方法的全部内容。对不起,我太天真了,因为我来晚了。我一直在阅读有关构建表达式以支持动态修改可执行代码的内容。当从给定的表达式树发出lambda表达式时,只要输入/环境不同(比如任何给定的常量/参数/成员表达式的不同值),这对我来说都是有意义的。我想,如果我可以缓存(重用)从表达式树生成/编译的lambda,并且环境没有变化,那将是理想的 问题:即使环境没有变化,CLR是否始终发出lambda表达式?如果是这样的话,最好的办法是在环境没有变化的情况下

这是关于
Expression
类型的
Compile
方法的全部内容。对不起,我太天真了,因为我来晚了。我一直在阅读有关构建表达式以支持动态修改可执行代码的内容。当从给定的表达式树发出lambda表达式时,只要输入/环境不同(比如任何给定的常量/参数/成员表达式的不同值),这对我来说都是有意义的。我想,如果我可以缓存(重用)从表达式树生成/编译的lambda,并且环境没有变化,那将是理想的


问题:即使环境没有变化,CLR是否始终发出lambda表达式?如果是这样的话,最好的办法是在环境没有变化的情况下避免从lambda编译表达式?

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

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


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

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

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


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

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;
}
public Func Get(表达式)
{
string key=expression.Body.ToString();
Func结果;
if(!\u cache.TryGetValue(键,输出结果)){
结果=expression.Compile();
_cache.Add(键、结果);
}
返回结果;
}

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;
}
public Func Get(表达式)
{
string key=expression.Body.ToString();
Func结果;
if(!\u cache.TryGetValue(键,输出结果)){
结果=expression.Compile();
_cache.Add(键、结果);
}
返回结果;
}

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

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

我认为这取决于您使用表达式树的确切方式。你能举一个具体的例子来说明你是如何使用它们的吗?我想这取决于你到底是如何使用表达式树的。你能举一个具体的例子来说明你是如何使用它们的吗?我认为用
ToString()
来比较等式是很脆弱的。例如,您可以创建两个
表达式
,这两个表达式都具有
(x,x)=>(x+x)
的字符串表示形式,但将返回不同的结果。尽管替代方法(从结构上正确比较
表达式
s)并不简单。而且,您的代码实际上不会工作,因为您不能有通用字段(
\u cache
必须是
字典
,这是不可能的)。但这很容易通过强制转换来解决。我认为使用
ToString()
来比较相等性是很脆弱的。例如,您可以创建两个
表达式
,这两个表达式都具有
(x,x)=>(x+x)
的字符串表示形式,但将返回不同的结果。尽管替代方法(从结构上正确比较
表达式
s)并不简单。而且,您的代码实际上不会工作,因为您不能有通用字段(
\u cache
必须是
字典
,这是不可能的)。你的评论让我质疑我是否应该使用Compile(),但我也发现了一些帖子说应该没有内存泄漏——有一些情况下它会出现,而另一些情况下它不会出现?你能解释一下吗。提前谢谢!一篇帖子的例子:你的评论让我质疑我是否应该使用Compile(),但我也发现一些帖子说应该没有内存泄漏,是吗