上次把实实在在的源代码生成了,不再是抽象的东西了,现在要用程序动态的来编译上次的东西。
System.CodeDOM.Compiler中的三个接口都是从CodeDomProvider得到的(上次强调过了),这次用到的接口是ICodeCompiler,所以先:
CSharpCodeProvider provider = new CSharpCodeProvider();
ICodeCompiler compiler = provider.CreateCompiler();
这个接口的方法全是CompileAssemblyFrom*这种形式的,分别是*FromDom*、*FromFile*、*FromSource*,从此可以看出动态编译不只是从文件来编译,它可以直接从生成的CodeDOM直接编译,也可以从包含源代码的字符串编译(FromSource这个)。而且它可以一起编译好几个的,如CompileAssemblyFromDomBatch,当然也有*FileBatch之类的。
编译与生成写法很象的,得到接口后就是一个*From*的方法也就好了。以CompileAssemblyFromFile为例:
CompilerResults CompileAssemblyFromFile(
CompilerParameters options,
string fileName
);
也是一个参数作为源,这里就是fileName了;目的地,生成代码的时候是有个TextWrite类型的参数作为去向,而这里就是返回一个CompilerResults类型表示编译后的结果;在生成的时候有个CodeGeneratorOptions,这里也有一个options,只是类型不同而已,这里是CompileParameters,而编译的奥秘当然也就在这个CompileParameters里了。
上面这个CompileParameters里设置的属性是很丰富,不过我没怎么玩过编译器,有些也不知道怎么跟编译时的哪个选项相对应。
string CompilerOptions:第一个就搁浅了,options这个概括太笼统,它说是附加的命令行参数,我不知道它是应该对应编译器选项里的哪些,难道是只要下面没有涉及过的选项都用这个来设吗?
Evidence Evidence:这个是证据,安全方面的东西,很少涉及,不太懂。
bool GenerateExecutable:是不是生成可执行文件,默认下是false,就是生成DLL文件。
bool GenerateInMemory:编译时在内存输出,默认情况下是不是的,都是输出到硬盘,大家大多也是这么干。要输出到内存设为true就是了。
bool IncludeDebugInformation:要不要生成调试信息,这没什么好说的。
string MainClass:主类的名称。
string OutputAssembly:输出程序集名称。
StringCollection ReferencedAssemblies:引用程序集的名称。
TempFileCollection TempFiles:临时文件。
bool TreatWarningsAsErrors:是否将警告视为错误。
IntPtr UserToken:创建编译器进程时的用户标记,这个不是很懂。
int WarningLevel:中止编译的警告等级。
string Win32Resource:要连接到已编译程序集中的Win32资源文件。
可设置的属性是比较的多,但比起编译器的选项来好象还是有点小巫见大巫。看来很多东西是在刚开始的那个CompileOptions设置的。
一般情况下很多属性都保持默认就是了,直接在构造函数里把最需要的东西设置出来就是了如下:
//编译参数
CompilerParameters cp = new CompilerParameters(new string[] {"System.dll"},
filepath.Substring(0,filepath.LastIndexOf(".") + 1) + "exe",false);
cp.GenerateExecutable = true;//生成EXE,不是DLL
上面的这个构造函数是全的构造函数了(再要设属性就只能new后再设置了)。第一个参数就是引用的程序集,第二个是输出文件名,第三个是要不要调试信息。
再来一句:
CompilerResults cr = compiler.CompileAssemblyFromFile(cp,filepath);
编译结束。
用那个文件或CodeDOM或一堆源代码的字符串编译成功了没有,可以从返回的那个CompilerResults来查看。这个CompileResults里也有好多东西可以查看的。
CompiledAssembly 属性指示编译的程序集。
Evidence 属性指示程序集的安全证据。
PathToAssembly 属性指示编译的程序集的路径(如果不是只在内存中生成编译的程序集)。
Errors 属性指示任何编译器错误和警告。
Output 属性包含编译器输出消息。
NativeCompilerReturnValue 属性指示编译器返回的结果代码值。
TempFiles 属性指示编译和链接过程中生成的临时文件。
这些都很简单的。至此编译的任务结束。
动态生成与编译的基本的东西没了,主要的麻烦在生成CodeDOM上,难倒不难,就是长长的代码麻烦一点。生成源代码与动态编译基本没有什么亮点。
不过下面还有个ICodeParser没有实现的,这里好象有点难度的,有点字符串分析的味道。前两天在CodeProject找到了一个,不过那个是两年前的代码,那时的Nunit跟现在的好象都不一样,我把它的单元测试部分拿掉才能编译通过。现在要分析一下它那个东西,不知有什么收获。