代码的动态编译并执行是一个。NET平台提供给我们的很强大的工具用以灵活扩展(当然是面对内部开发人员)复杂而无法估算的逻辑,并通过一些额外的代码来扩展我们已有 的应用程序。这在很大程度上给我们提供了另外一种扩展的方式(当然这并不能算是严格意义上的扩展,但至少为我们提供了一种思路)。
动态代码执行可以应用在诸如模板生成,外加逻辑扩展等一些场合。一个简单的例子,为了网站那的响应速度,HTML静态页面往往是我们最好的选择,但基于数据驱动的网站往往又很难用静态页面实现,那么将动态页面生成html的工作或许就是一个很好的应用场合。另外,对于一些模板的套用,我们同样可以用它来做。另外这本身也是插件编写的方式。
最基本的动态编译
.Net为我们提供了很强大的支持来实现这一切我们可以去做的基础,主要应用的两个命名空间是:System.CodeDom.Compiler和Microsoft.CSharp或Microsoft.VisualBasic。另外还需要用到反射来动态执行你的代码。动态编译并执行代码的原理其实在于将提供的源代码交予CSharpCodeProvider来执行编译(其实和CSC没什么两样),如果没有任何编译错误,生成的IL代码会被编译成DLL存放于于内存并加载在某个应用程序域(默认为当前)内并通过反射的方式来调用其某个方法或者触发某个事件等。之所以说它是插件编写的一种方式也正是因为与此,我们可以通过预先定义好的借口来组织和扩展我们的程序并将其交还给主程序去触发。一个基本的动态编译并执行代码的步骤包括:
· 将要被编译和执行的代码读入并以字符串方式保存
· 声明CSharpCodeProvider对象实例
· 调用CSharpCodeProvider实例的CompileAssemblyFromSource方法编译
· 用反射生成被生成对象的实例(Assembly.CreateInstance)
· 调用其方法
以下代码片段包含了完整的编译和执行过程:
//get the code to compile string strSourceCode = this.txtSource.Text; // 1.Create a new CSharpCodePrivoder instance CSharpCodeProvider objCSharpCodePrivoder = new CSharpCodeProvider(); // 2.Sets the runtime compiling parameters
by crating a new CompilerParameters instance CompilerParameters objCompilerParameters = new CompilerParameters(); objCompilerParameters.ReferencedAssemblies.Add("System.dll"); objCompilerParameters.ReferencedAssemblies.Add("System.Windows.Forms.dll"); objCompilerParameters.GenerateInMemory = true; // 3.CompilerResults: Complile the code snippet
by calling a method from the provider CompilerResults cr = objCSharpCodePrivoder.
CompileAssemblyFromSource(objCompilerParameters,
strSourceCode); if (cr.Errors.HasErrors) { string strErrorMsg = cr.Errors.Count.ToString() + " Errors:"; for (int x = 0; x < cr.Errors.Count; x++) { strErrorMsg = strErrorMsg + "\r\nLine: " + cr.Errors[x].Line.ToString() + " - " + cr.Errors[x].ErrorText; } this.txtResult.Text = strErrorMsg; MessageBox.Show("There were build erros, please modify your code.",
"Compiling Error"); return; } // 4. Invoke the method by using Reflection Assembly objAssembly = cr.CompiledAssembly; object objClass = objAssembly.CreateInstance("Dynamicly.HelloWorld"); if (objClass == null) { this.txtResult.Text = "Error: " + "Couldn't load class."; return; } object[] objCodeParms = new object[1]; objCodeParms[0] = "Allan."; string strResult = (string)objClass.GetType().InvokeMember( "GetTime", BindingFlags.InvokeMethod, null, objClass, objCodeParms); this.txtResult.Text = strResult;