我曾开发过一个.NET应用,需要在运行中动态的创建EXE文件,当时查了很多资料后才顺利实现,有一些心得,整理出来。
要从你的.NET应用程序中动态的生成一个汇编你有两种选择:CodeDom和Reflection.Emit。在这篇文章中我将比较二者的不同,以便你作出哪种方法才是最适合你的选择。
CodeDom和Reflection.Emit的最大不同就是CodeDom可以产生C#、VB.NET、Visual J#,以及其他.NET语言,而Reflection.Emit则是产生原始的中间语言(IL—Intermediate Language)语句。CodeDom不需要深入的IL知识,因此使用起来会容易的多。不利的方面是你被限制为一套所有语言都可以访问的动作和类型集,这消除了对超载操作或是语言专有的方言的使用。有利的方面是通过使用一个不同的代码提供者,你可以很容易的在不同的输出语言间进行切换。
Reflection.Emit是极端强大的,它可以做许多CodeDom不能做的事情,因为它允许你编写任何合法的IL语句。Reflection.Emit的最大障碍就是它要求你理解并能够编写MSIL。Reflection.Emit为你提供了把项目连同真正的MSIL指令压入堆栈和从堆栈中弹出的能力。这在某些时候是有益的,但它也可能成为一个缺点,因为这失去了更高级语言所提供的高效的生产力。
这个代码例子(C#写的)利用CodeDom建立了一个带有一个返回的Main,这个CodeDom产生一个高级的.NET语言:
CodeEntryPointMethod main = new CodeEntryPointMethod();
main.Statements.Add(new System.CodeDom.CodeMethodReturnStatement());
这是同样的Main,但是是用Reflection.Emit来建立的,它产生原始的IL:
MethodBuilder methodbuilder = typeBuilder.DefineMethod("Main", MethodAttributes.HideBySig | MethodAttributes.Static | MethodAttributes.Public, typeof(void), new Type[] { typeof(string[]) });
ILGenerator ilGenerator = methodbuilder.GetILGenerator();
ilGenerator.Emit(OpCodes.Ret);
assemblyBuilder.SetEntryPoint(methodbuilder, PEFileKinds.ConsoleApplication);
这里有一些明显的不同:CodeDom有一个类可以用来创建一个入口点,而Reflection.Emit代码必须手工建立方法并设置入口点。来看看返回:再一次,CodeDom有一个类可以用来做这个工作,而Reflection.Emit代码必须为返回而输出IL指令。
你到底应该使用CodeDom还是Reflection.Emit依赖于你的情况。
在下列情况中你应该使用CodeDom:
- 编写一个代码生成工具。
- 任何时候当你希望产生C#、VB.NET或任何其他高级语言时。
- 使用C#或VB.NET代码片断时。
在下列情况中你应该使用Reflection.Emit:
- 需要严密的控制IL的优化。
- 要执行不服从公共语言规范的动作,比如不安全的代码。
假如你的汇编生成工作不属于前面所述类目中的一个,而你只需要动态的生成汇编,则你可以使用这些生成技术中的任何一种。在大多数的情况下,CodeDom将是更好的选择,因为它更容易使用,并且可以生成许多不同的语言。
这二者都是强大的工具,用于不同的场合。你只需遵循这一基本的指导方针:如果你正在编写工具或是需要更高级的代码生成,则使用CodeDom;如果你需要对你的生成汇编进行低级控制,则使用Reflection.Emit。