手机版

介绍中的动态编译技术 NET详细描述

时间:2021-11-14 来源:互联网 编辑:宝哥软件园 浏览:

代码的动态编译和执行是。NET平台为我们灵活地扩展(当然,对于内部开发人员来说)复杂且无法计算的逻辑,并通过一些额外的代码来扩展我们现有的应用程序。这在很大程度上为我们提供了另一种拓展的方式(当然,这不是严格的拓展,但至少为我们提供了一种思维方式)。动态代码执行可以应用于模板生成和逻辑扩展等场合。举个简单的例子,html静态页面往往是我们对于网站响应速度的最佳选择,但是数据驱动的网站往往很难用静态页面来实现,所以动态页面生成HTML的工作可能是一个很好的应用场合。另外,对于一些模板的应用,我们也可以使用它。另外,这也是编写插件的方式。最基本的动态编译,Net,为我们实现我们所能做的基础提供了强有力的支持。主要使用的两个名称空间是:System。编译器和微软。您还需要使用反射来动态执行您的代码。实际上,动态编译和执行代码的原理在于将提供的源代码提交给CSharpCodeProvider进行编译(实际上和CSC没有什么区别)。如果没有编译错误,生成的IL代码将被编译成DLL,存储在内存中,加载到应用程序域中(默认为当前),并通过反射调用方法或触发事件。之所以说它是编写插件的一种方式,正是因为如此,我们可以通过预定义的借口来组织和扩展我们的程序,并将其返回给主程序进行触发。动态编译和执行代码的基本步骤包括:读取要编译和执行的代码,并将其保存为字符串;声明CSharpCodeProvider对象的实例;调用CSharpCodeProvider实例的CompileAssemblyFromSource方法进行编译;通过反射(程序集)生成生成的对象的实例。Cr)。以下代码片段包含完整的编译和执行过程:复制代码如下://获取要编译的代码字符串strsource code=this . txt source . text;//1.创建一个新的csharpcode privoder instancecsharpcode provider objcsharpcode privoder=new csharpcode provider();//2.通过编写新的编译器参数实例来设置运行时编译参数对象编译器参数=新的编译器参数();objCompilerParameters。referenceassemblies . Add(' system . dll ');objCompilerParameters。参考组件。添加('系统。windows . forms . dll ');objCompilerParameters。GenerateInMemory=true//3.CompilerResults:通过从provider compiler results Cr=Objcsharpcode privoder调用方法来编译代码段。compilessemblyfromsource(ObjcompilerParameters,strSourcecode);if (cr。错误。HasErrors){ string strErrorMsg=Cr。错误。count . tostring()“error s 3360”;for(int x=0;x cr。错误。计数;x){ strErrorMsg=strErrorMsg ' \ r \ n line : ' Cr。错误[x].Line.ToString() ' - ' cr。错误[x]。错误文本;} this . txtresult . text=strErrorMsg;消息框。显示('有生成错误,请修改您的代码。'、“编译错误”);返回;}//4.使用ReflectionAssembly Objassembly=Cr调用该方法。CompiledAssemblyobject objClass=objAssembly。CreateInstance('Dynamicly。hello world’);if(Objclass==null){ this . txtresult . text=' error : ' '无法加载类。返回;}对象[] objCodeParms=新对象[1];对象代码参数[0]='艾伦';string strResult=(string)Objclass。GetType()。InvokeMember('GetTime ',BindingFlags。InvokeMethod,null,objClass,ObjcodeParms);this . txtresult . text=strResult;这里需要说明的是,我们在传递编译参数时将GenerateInMemory设置为true,这意味着生成的DLL将被加载到内存中(然后默认引用到当前应用程序域中)。在调用GetTime方法时,我们需要添加参数,传递一个对象类型的数组,并通过InvokeMember of Reflection调用它。

在生成的程序集中创建对象实例时,需要注意使用的命名空间是输入代码的真实命名空间。以下是我们输入的测试代码(为方便起见,所有代码均为外部输入,动态执行时不做调整):复制代码的代码如下:使用系统;命名空间dynamic ly { public class hello world { public string GetTime(string strName){ return ' Welcome ' strName ',Check in at ' System。DateTime . now . tostring();}}}运行附件中提供的程序很容易得到如下结果:

现在一切看起来都很好,我们可以编译代码并将其加载到当前的应用程序域中以参与我们的活动,但是您想在过去卸载这个程序吗?控制程序更好?另外,当你多次运行这个程序时,你会发现它占用了大量的内存,每次执行都会增加内存的使用。有必要解决这个问题吗?当然,否则,你会发现这个东西没用。我需要执行的一些大型应用程序会让我的服务器崩溃,让我发疯。要解决这个问题,我们需要了解应用领域。那个。NET应用程序域是由提供的容器。NET运行并托管一个活动进程,该进程将进程所需的代码和数据隔离到一个小范围内,称为应用程序域。当应用程序运行时,应用程序域将所有程序集/组件集加载到当前应用程序域中,并根据需要调用它们。至于动态生成的代码/程序集,我们似乎没有办法管理它。否则,我们可以使用管理应用程序域提供的程序集的方法来动态加载和移除程序集,以提高性能。具体怎么做,在前一步的基础上增加以下步骤:创建另一个Application Domain,动态创建(编译)代码并保存到磁盘,创建公共远程调用接口,创建远程调用接口的实例。并通过这个接口访问它的方法。换句话说,对象被加载到另一个AppDomain中,并由远程调用的方法调用。所谓的远程调用实际上是跨应用程序域的调用,所以这个对象(动态代码)必须从MarshalByRefObject类继承。为了复用,在一个项目中单独提到了这个接口,并提供了一个工厂来简化每一个调用操作:复制代码如下:使用System使用系统。集合。通用;使用系统。Linq使用系统。文字;使用系统。反思;可以在远程AppDomain边界上运行的命名空间RemoteAccess{ ///summary ///接口。////摘要公共接口IRemoteInterface { object Invoke(string lcMethod,object[]Parameters);}///summary////Factory类创建公开IRemoteInterface ////summary公共类RemoteLoaderFactory : marshalbyref object { private const binding flags bfi=binding flags。实例|绑定标签。Public | BindingFlags。CreateInstancepublic remote loaderfactory(){ } public iremote interface Create(string assembly file,string typeName,object[]constructor GS){ return(iremote interface)Activator。createinstance from(assembly file,typeName,false,bfi,null,constructArgs,null,null,null)。unwrap();}}}需要在原基础上修改的是:将编译好的DLL保存到磁盘。创建另一个AppDomain。获取IRemoteInterface接口的引用。(将生成的DLL加载到附加的appdomain中)。调用InvokeMethod方法来远程调用它。您可以通过AppDomain卸载程序集。Unload()方法。下面是演示如何应用此方案的完整代码。

复制代码代码如下://获取要编译的代码strSourceCode=this。txt源。文本;//1.创建一个附加的AppDomainAppDomainSetup Objsetup=new AppDomainSetup();objSetup .ApplicationBase=AppDomain .CurrentDomain。base directoryapdomain objapdomain=AppDomain .CreateDomain('MyAppDomain ',null,Objsetup);//1.创建一个新的csharp code privoder instance sharp code提供程序objcsharp code privoder=new csharp code provider();//2.通过编写新的编译器参数实例来设置运行时编译参数对象编译器参数=新的编译器参数();objCompilerParameters .参考组件。添加('系统。dll ');objCompilerParameters .参考组件。添加('系统窗户。表格。dll ');//加载远程加载器接口对象编译器参数.参考组件。添加('远程访问。dll ');//将生成的程序集加载到memoryobjCompilerParameters参数中GenerateInMemory=FastObjcompilerParameters .OutputAssembly='动态代码。dll ';//3.编译器结果:通过从提供程序编译器结果Cr=Objcsharpcode privoder调用方法来编译代码段.compilessemblyfromsource(ObjcompilerParameters,strSourcecode);if (cr .错误HasErrors){ string strErrorMsg=Cr .错误伯爵。tostring()“错误s 3360”;for(int x=0;x cr .错误。计数;x){ strErrorMsg=strErrorMsg ' \ r \ n第:行' Cr .错误[x].Line.ToString() ' - ' cr .错误[x].错误文本;}这个。txtresult。text=strErrorMsg消息框。显示('有生成错误,请修改您的代码。'、"编译错误");返回;}//4.通过使用反射远程加载工厂工厂=(RemoteLoaderFactory)Objapdomain调用该方法CreateInstance('RemoteAccess ',' RemoteAccess ' .远程加载工厂').unwrap();//在工厂的帮助下,创建一个真正的live class'instance对象objobobject=factory .创建(“DynamicalCode.dll”,“动态地.HelloWorld ',null);if(ObJobject==null){ this。txtresult。文本=“错误:”无法加载类。返回;}//***将对象转换为远程接口,避免加载类型infoIRemoteInterface ObjRemote=(IRemoteInterface)objob对象;对象[] objCodeParms=新对象[1];对象代码参数[0]='艾伦;字符串strResult=(字符串)ObjRemote .Invoke('GetTime ',ObjcodeParms);这个。txtresult。text=strResult//处置对象,卸载生成的dll,DLLs.objRemote=nullAppDomain .卸载(ObJapdomain);系统IO。文件。删除('动态代码。dll ');对于客户端的输入程序,我们需要继承于MarshalByRefObject类和IRemoteInterface接口,并添加对远程访问程序集的引用。以下为输入:复制代码代码如下:使用系统;使用系统。反思;使用远程访问;命名空间dynamic ly {公共类hello world : marshalbyref对象,IRemoteInterface {公共对象Invoke(string strMethod,object[]Parameters)} }返回此GetType().InvokeMember(strMethod,BindingFlags .InvokeMethod,null,this,Parameters);}公共字符串GetTime(字符串strName){ 0返回"欢迎“strName,在"系统"处签入日期时间。现在。tostring();} }}这样,你可以通过适时的编译,加载和卸载程序集来保证你的程序始终处于一个可控消耗的过程,并且达到了动态编译的目的,而且因为在不同的应用程序域中,让你的本身的程序更加安全和健壮。示例代码下载:http://小哉。JB 51。net/201311/马援/DynamicCompiler(jb51.net).rar

版权声明:介绍中的动态编译技术 NET详细描述是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。