我想知道编译期间Expression
类及其子类(UnaryExpression
,MemberExpression
等)的属性是否折叠为内联常量对象?
例如在ASP.Net MVC中,视图模型的强类型属性名称(例如Html.LabelFor(vm => vm.FirstName))
和Html.LabelFor
)的常见做法是期望一个表达式,然后它会检查{ {1}}等等。这些属性是在运行时评估的(使用运行时编译成本等)还是在编译期间进行了优化(如.Net 4.5中的Body.Member.Name
)?
答案 0 :(得分:2)
“评估”是什么意思?
它没有编译。所有必要的信息都已在编译期间准备好。所以从这个意义上讲,它并没有被多次“评估”。表达式没有运行时编译,这不是表达式树的含义(除非你明确地调用Compile
,它基本上将表达式树转换为方法委托)。
但是,每次都创建表达式树 :
for (int i = 0; i < 1000000; i++)
{
Expression<Func<DataTable, string>> e = t => t.TableName;
}
在循环的每一步中,表达式一次又一次地创建,即使它是可证明的常量(所有Expression
都是不可变的,并且没有闭包)。
这不是 free ,但它也不是非常昂贵 - 这个示例代码每秒产生大约17万个表达式(这是一个非常愚蠢的基准,但对于球场估计并不是那么糟糕) )。
部分成本是必须创建的所有Expression
个实例。有些是涉及的反思。更简单的表达式 更容易创建。
但是,您还需要考虑周围的成本:
如果你发现它在某个地方(配置文件,配置文件,配置文件)导致瓶颈,你通常可以缓存表达式树 - 正如我所说,它们是不可变的,所以这是安全且容易的。实际上,您可以轻松地创建具有要在运行时替换参数的巨大表达式树 - 由于表达式的不变性,您只需要创建实际必须替换的表达式 - 其余的将保持不变。
对于ASP.NET MVC,您使用这样的方法可以轻松访问对象元数据。您在这里获得的巨大好处是对您引用的属性和字段进行编译时检查 - 这使得保持应用程序一致性变得容易得多,同时代码非常容易阅读(“它是FirstName属性的标签,duh。“)和写。它将通过重构与您保持联系。
答案 1 :(得分:1)
您可以使用任何反编译器或linqpad查看反编译代码。它将创建一系列Expression.XXX
调用以在运行时构建表达式。
例如,以下C#代码:
Expression<Func<string>> foo = () => "";
编译成:
IL_0000: nop
IL_0001: ldstr ""
IL_0006: ldtoken System.String
IL_000B: call System.Type.GetTypeFromHandle
IL_0010: call System.Linq.Expressions.Expression.Constant
IL_0015: ldc.i4.0
IL_0016: newarr System.Linq.Expressions.ParameterExpression
IL_001B: call System.Linq.Expressions.Expression.Lambda
IL_0020: stloc.0 // foo
IL_0021: ret
答案 2 :(得分:1)
重新创建它们......即使在最简单的情况下,也会重新创建Expression
:
static Expression<Func<int, int>> otherFunc;
public static void MyMethod(Expression<Func<int, int>> func)
{
if (otherFunc == null)
{
otherFunc = func;
Console.WriteLine("First time");
}
else
{
Console.WriteLine("Same Expression<Func<int>>: {0}", object.ReferenceEquals(func, otherFunc));
Console.WriteLine("Same Parameter: {0}", object.ReferenceEquals(func.Parameters[0], otherFunc.Parameters[0]));
}
}
和
for (int i = 0; i < 2; i++)
{
MyMethod(x => 5);
}
结果:
First time
Same Expression<Func<int>>: False
Same Parameter: False
我们在一个方法中,我们在MyMethod
内调用for
,而Expression
每次都不同。