我想在C#中理解AST。我想知道,这个例子中的Compile()
方法究竟是什么。
// Some code skipped
Expression<Func<string, int, int, string>> data = Expression.Lambda<Func<string, int, int, string>>(
Expression.Call(s, typeof(string).GetMethod(“Substring”, new Type[] { typeof(int), typeof(int) }), a, b),
s, a, b
);
Func<string, int, int, string> fun = data.Compile();
为了防止误解,我了解Expression.Lambda
和Expression.Call
结构。我感兴趣的是Compile()
方法。它以某种方式生成真正的MSIL?我能看到MSIL吗?
答案 0 :(得分:49)
我感兴趣的是
Compile()
方法。它以某种方式生成真正的MSIL?
是。 Compile方法在lambda body块上运行访问者,并为每个子表达式动态生成IL。
如果您有兴趣自己学习如何吐IL,请参阅this "Hello World" example of how to use Lightweight Codegen。 (我注意到,如果您处于部分受信任的应用程序域中必须使用Lightweight Codegen的不幸位置,那么在具有限制性跳过可见性的世界中,事情会变得有点奇怪;如果您感兴趣,请参阅Shawn Farkas's article 。)
我可以看到MSIL吗?
是的,但你需要一个特殊的“可视化器”。我在实现我的部分时用于调试Compile()
的可视化工具可以在这里下载:
http://blogs.msdn.com/b/haibo_luo/archive/2005/10/25/484861.aspx
答案 1 :(得分:9)
表达式表示表达式树形式的数据结构 - 使用Compile()
这个表达式树可以以委托形式编译成可执行代码(这是一种“方法”调用)。
编译后,您可以正常调用委托 - 在您的示例中,委托是Func<string,int,int,string>
。当您基于仅在运行时可用的数据动态创建表达式树时,可能需要此方法,其最终目标是创建和执行相应的委托。
您无法看到代理人的“代码”。它所基于的表达式树本身就是最接近的。
答案 2 :(得分:7)
这个问题的答案现在已经过时了,因为现在有时只会发生这种情况。
向IL编译表达式需要Reflection.Emit,它始终不可用,特别是AOT。因此,在这些情况下,不是编译到IL,而是将表达式“编译”为表示指令的对象列表。这些指令中的每一个都有一个Run
方法,它使得它执行适当的操作,处理一堆值,就像IL在堆栈上工作一样。然后可以将一个在这些对象上调用Run
的方法作为委托返回。
通常运行这样的委托比使用IL更慢,但是当编译到IL时,它是唯一的选项,并且编译步骤通常更快,因此编译+运行的总时间通常较少。翻译而不是IL用于一次性表达。
出于这个原因,在.NET Core中,现在有一个Compile
的重载,即使编译为IL,也需要一个布尔请求的解释。
所有这些都构成了有趣的语言组合;表达式本身是一种语言,程序集用C#编写,它可以编译成IL,解释的指令对象构成第四种语言。