Expression.Compile中捕获的内容

时间:2016-02-05 11:24:46

标签: c# lambda expression expression-trees

假设我有一个带有属性栏的Type Foo类。

我得到了以下方法:

public static void DumpValue<T>(Expression<Func<T>> expr)
{
     MemberExpression memberExpression = expression.Body as MemberExpression;
     Debug.WriteLine("{0} => {1}", memberExpression.Member.Name, expr.Compile()());
}

它的用法如下:

Foo a = new Foo{Bar ="Hello"};
Foo b = new Foo{Bar ="World"};
DumpValue(() => a.Test);
DumpValue(() => b.Test);

给出了输出:

Bar => Hello
Bar => World

我的问题涉及连续编译调用。它是否足够聪明以重新修改Func&lt; T&gt; 像Func&lt; Foo,T&gt;这样的东西(内部)所以实例被删除所以它可以用于Foo的任何实例,只有结果的Delegate是实例特定的?或者确实为每个实例编译完全

如果是后者,我是否需要担心使用大量编译函数来污染内存,我看不出我的测试有什么影响。

我知道通过重写DumpValue可以避免这种情况,但我想知道场景背后会发生什么。这只是一个例子来说明。

我在Source中挖掘了我的方法,却找不到任何线索。

改述这个问题: 编译器是否优化了实例并在此处缓存了一些信息,并且仅将实例烘焙到最终代理中,或者他是否“一直”“

2 个答案:

答案 0 :(得分:2)

  

我的问题涉及编译。是否足够聪明才能返工   Func进入类似Func的东西,以便删除实例   所以它可以用于任何Foo的实例?或者它确实编译   它适用于每个实例?

编译表达式时,您将获得委托。如果要在调用已编译的表达式时使用任意实例,则不需要Expression<Func<T>>Expression<Func<T, S>>

否则,您需要从头开始构建表达式树,以便不在表达式主体中使用捕获的引用。

关于缓存的事情......

我已修改您的代码以检查即使您编译两次相同的表达式树,您也会得到不同的委托实例:

using System;
using System.Linq.Expressions;

public class Program
{
    public static Delegate DumpValue<T>(Expression<Func<T>> expr)
    {
        MemberExpression memberExpression = expr.Body as MemberExpression;

        return expr.Compile();
    }

    public static void Main()
    {
        string a = "foo";
        string b = "bar";

        var del1 = DumpValue(() => a);
        var del2 = DumpValue(() => b);

        // FALSE
        Console.WriteLine(Object.ReferenceEquals(del1, del2));
    }
}

简而言之,没有缓存,正如我的回答从一开始就说明的那样,我怀疑这样的功能是否易于实现,因为它可能是非常有用的情况和边缘情况,其中泛化表达式可能是时间 - 消耗任务。 在大多数情况下,许多委托实例可能比实现缓存算法更好(甚至在编译之前进行表达式树转换......)。

答案 1 :(得分:2)

我会说任何地方都没有缓存。

Expression<TDelegate>.Compile()来电LambdaCompiler.Compile()。从那里创建了一个新的LambdaCompiler实例,初始化_method字段(带有var method = new DynamicMethod(lambda.Name ?? "lambda_method", lambda.ReturnType, parameterTypes, true);),然后用于首先创建方法,然后创建委托。请注意,_method是只读的,因此没有人可以更改它,LambdaCompiler.CreateDelegate()直接使用它来创建委托。在任何地方都没有缓存。