假设我有一个带有属性栏的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中挖掘了我的方法,却找不到任何线索。
改述这个问题: 编译器是否优化了实例并在此处缓存了一些信息,并且仅将实例烘焙到最终代理中,或者他是否“一直”“
答案 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()
直接使用它来创建委托。在任何地方都没有缓存。