艾伟:C#代码动态编译、动态执行、动态调试

  前几天看到一篇关于.net动态编译的文章 .NET中的动态编译 ,很受启发。在此基础上我做了一些封装,为使调用更加简单,并增加了对动态代码调试的支持,相同代码只编译一次的支持,代码改动自动重新编译,代码引用文件的自动加载和手工加载等功能。

   

      如上图,我封装的类CSharpProvider很简单,下面说明一下一些公共成员的用法。

      公共属性

      AssemblyFileName:这个属性指定动态编译后生成的配件名称。

      CompilerParameters:这个属性指定编译的参数

      References:这个属性指定被编译代码中的引用。调用者只要调用References.Add("xxx.dll"),就可以加入自己的引用,对于System命名空间的所有引用,不需要手工加入,该类会自动加载。对于用户自己的组件,如果不手工指定引用文件,该类会自动根据名字空间名进行猜测。

      SourceCodeFileEncoding:如果以文件形式编译,指定文件的编码类型。

      公共方法

      public bool Compile(string code)  

      输入代码字符串,并编译

      public bool CompileFromFile(string sourceCodeFileName)

      编译输入的代码文件

      public object CreateInstance(string code, string typeFullName)

      创建类的实例

      如下面代码,可以输入 CreateInstance(code, "MyInterface.IHelloWorld"),也可以输入CreateInstance(code, "HelloWorld"),程序会根据

类型名称来自动找到符合条件的类并实例化。如果代码中有多个指定类型的类,将实例化第一个。

using System;
using MyInterface;

[Serializable]
public class HelloWorld : MarshalByRefObject, IHelloWorld
{
    public string Say()
    {
        return "Hi";
    }
}

 

    这里需要特别指出的是由于用到了AppDomain的远程调用,所有的动态加载的代码必须继承自MarshallByRefObject

如果仅仅声明为[Serializable] 虽然也可以执行,但主应用程序域会记录下子应用程序域的一个引用,这样导致子应用程序

域卸载后,依然无法完全释放内存,从而内存泄漏。所以这个很关键,一定要注意。

      public object CreateInstanceFromFile(string fileName, string typeFullName)

      从文件创建动态实例

      下面再谈谈对动态代码的调试

      动态创建的代码如果不能调试,就像一个黑盒子,对系统的可维护性有较大破坏。未来实现这个功能,我们需要做以下工作,

第一、编译时要生成调试信息,这个可以通过设置 CompilerParameters.IncludeDebugInformation = true;来实现

第二、我们必须告诉调试器源码对应的位置,对于从文件编译的情况,源码文件位置会被自动写入调试信息文件 *.pdb中,而对于从内存编译的情况,我还没有找到指定的方法,如果哪位朋友知道,还望赐教。所以目前如果要调试动态代码,必须从文件编译,也就是调用CompileFromFile,CreateInstanceFromFile

 第三、我们需要在代码中设置一个断点,这个可以在代码中加入 System.Diagnostics.Debugger.Break(); 来解决。

 如下图所示,动态代码现在可以调试了。

 

 

 应用程序域

 为了避免内存泄漏,本程序封装了对应用程序域的使用,调用者基本不需要关心应用程序域的调用和卸载过程。本程序在

重新编译或者对象销毁时会自动卸载应用程序域,从而释放内存。由于做这个程序是在应用程序域上遇到了很多麻烦,所以

感觉还是有必要简单讲一下应用程序域。

 

 

 如上图所示,应用程序与实际上有点像一个单独的进程,但这个进程是运行在当前进程里面的,当然这个比喻不够贴切。

对应用程序域的调用有点类似进程间采用 Remoting 方式的对象调用,也就是说默认应用程序域要调用其他应用程序域中的对象,

必须采用远程调用的方法,而不能直接调用,如果直接调用,默认应用程序域就会记录这个被调用的应用程序域的一个内存引用,

即使这个应用程序域执行了Unload 方法卸载后,内存依然无法释放,这也是我一开始操作应用程序域遇到的最大困扰。

另外所有暴露在两个应用程序域之间的类必须从MarshalByRefObject基础,这点非常重要,否则将导致内存无法释放。

本程序的一些缺陷

1、没有提供编译多文件的接口,其实要实现这个很简单,考虑到用于动态执行的代码脚本往往比较简单,所以偷懒没有做。

2、没有提供对动态代码中多个对象的枚举接口,以后再完善吧。

源码下载位置

源码下载 

 

时间: 2024-10-29 14:30:18

艾伟:C#代码动态编译、动态执行、动态调试的相关文章

利用CodeDom和反射动态编译并执行程序集

动态编译,听起来很酷,不是吗? 1.什么是动态编译 所谓动态编译是由两个字组成的:动态+编译.很显然,我们是想实现临时地 给出一段代码,然后将其编译成程序集(可以是在内存中,也可以是输出一个真 正的dll) 2.什么时候需要用到动态编译? 呃,这个问题有点难,简单地说,就是要动态的时候啦.呵呵.我们有的时候 需要提供系统这么一种灵活性,即有的类型没有办法预先写好,而是要根据情况 动态编译.这么说吧,例如我们希望根据数据库里面一个表的结构,动态编译出 来一个对应的类型. 3.如何使用动态编译.下面

C#代码的编译与执行过程(摘自ms inside c#)

编译|过程|执行 1.You write source code in C#. 2.You then compile it using the C# compiler (csc.exe) into an EXE. 3.The C# compiler outputs the MSIL code and a manifest into a read-only part of the EXE that has a standard PE (Win32-portable executable) head

Lua的编译、执行和调试技术介绍_Lua

dofile读入文件编译并执行,真正完成功能的函数是loadfile;与dofile不同,loadfile仅仅是编译代码成中间码,并且把编译后的chunk作为函数返回.如果发生错误,返回nil和错误信息.我们可以这么定义dofile: 复制代码 代码如下: function dofile(filename)      local f = assert(loadfile(filename))      return f() end 如果你只调用一次,可以使用dofile(filename),如果是

.NET中的动态编译

代码的动态编译并执行是一个.NET平台提供给我们的很强大的工具用以灵活扩展(当然是面对内部开发人员)复杂而无法估算的逻辑,并通过一些额外的代码来扩展我们已有 的应用程序.这在很大程度上给我们提供了另外一种扩展的方式(当然这并不能算是严格意义上的扩展,但至少为我们提供了一种思路). 动态代码执行可以应用在诸如模板生成,外加逻辑扩展等一些场合.一个简单的例子,为了网站那的响应速度,HTML静态页面往往是我们最好的选择,但基于数据驱动的网站往往又很难用静态页面实现,那么将动态页面生成html的工作或许

动态编译JAVA程序

编译|程序|动态 在Sun JDK 1.2及后续版本中,包含了一组可在程序运行时刻编译和执行Java代码的API.这些API被包含在tools.jar类库中.这个功能允许Java程序在运行时动态编译.执行小的代码块,在有些情况下这个功能会让Java应用程序的架构更加灵活.开放. 本文假定读者已经在计算机中安装并配置好了Sun JDK 1.2或更高的版本,并对javac编译器命令有所了解. 在Java程序中使用编译器 假定要使用javac命令编译 /home/mytest目录下Test.java文

C#动态编译及实现按钮功能动态配置

现在对做的系统要求要越来越灵活,功能配置越来越方便,牺牲一小部分的效率,而换取系统的灵活性,对于维护.功能扩展升级等工作提供了很大的方便. 前两天,一个项目要求界面上的按钮都是可以配置的,位置和功能都是可配置的.位置好说,用xml即可.但是功能可配置就有点难度了.如果说使用接口,那么参数则不好设置,而且就算用接口,在实际调用时,也得明确实例化哪个类.您可能还会说用反射,嗯,这的确是个好办法,但是还是在调用的时候,参数不确定,反射也就无用武之地了.查了半天,最终还是选择了动态编译. 用一个专门的类

详解.NET中的动态编译技术

代码的动态编译并执行是一个.NET平台提供给我们的很强大的工具用以灵活扩展(当然是面对内部开发人员)复杂而无法估算的逻辑,并通过一些额外的代码来扩展我们已有 的应用程序.这在很大程度上给我们提供了另外一种扩展的方式(当然这并不能算是严格意义上的扩展,但至少为我们提供了一种思路). 动态代码执行可以应用在诸如模板生成,外加逻辑扩展等一些场合.一个简单的例子,为了网站那的响应速度,HTML静态页面往往是我们最好的选择,但基于数据驱动的网站往往又很难用静态页面实现,那么将动态页面生成html的工作或许

Java理论与实践: 动态编译与性能测量

这个月,我着手撰写一篇文章,分析一个写得很糟糕的微评测.毕竟,我们 的程序员一直受性能困扰,我们也都想了解我们编写.使用或批评的代码的性能 特征.当我偶然间写到性能这个主题时,我经常得到这样的电子邮件:"我写的 这个程序显示,动态 frosternation 要比静态 blestification 快,与您上一 篇的观点相反!"许多随这类电子邮件而来的所谓"评测"程序,或者它们运行 的方式,明显表现出他们对于 JVM 执行字节码的实际方式缺乏基本认识.所以 ,在我着

C#动态编译,实现按钮功能动态配置

现在对做的系统要求要越来越灵活,功能配置越来越方便,牺牲一小部分的效率,而换取系统的灵活性,对于维护.功能扩展升级等工作提供了很大的方便. 前两天,一个项目要求界面上的按钮都是可以配置的,位置和功能都是可配置的.位置好说,用xml即可.但是功能可配置就有点难度了.如果说使用接口,那么参数则不好设置,而且就算用接口,在实际调用时,也得明确实例化哪个类.您可能还会说用反射,嗯,这的确是个好办法,但是还是在调用的时候,参数不确定,反射也就无用武之地了.查了半天,最终还是选择了动态编译. 用一个专门的类

详细介绍.NET中的动态编译技术_实用技巧

代码的动态编译并执行是一个.NET平台提供给我们的很强大的工具用以灵活扩展(当然是面对内部开发人员)复杂而无法估算的逻辑,并通过一些额外的代码来扩展我们已有 的应用程序.这在很大程度上给我们提供了另外一种扩展的方式(当然这并不能算是严格意义上的扩展,但至少为我们提供了一种思路). 动态代码执行可以应用在诸如模板生成,外加逻辑扩展等一些场合.一个简单的例子,为了网站那的响应速度,HTML静态页面往往是我们最好的选择,但基于数据驱动的网站往往又很难用静态页面实现,那么将动态页面生成html的工作或许