.Net开发笔记(八) 动态编译

虽然标题为“动态编译”,其实本文包含两个部分,一个就是标题说的动态编译源代码,另外一个应该是动态生成源代码。也就是说,在.Net中可以存在这样一种情况:在程序运行期间,由程序控制动态生成一份源代码(Source Code),然后再编译该源代码,生成一个新的程序集,紧接着再加载生成的程序集,最后运行。这个情况可能出现在很多地方,比如一些模板代码生成,像asp.net页面生成html,还可以在程序运行期间,由用户控制,动态的扩展程序功能。

要了解以上的实现过程,需先了解以下几个知识点:

  1. 反射
  2. 跨应用程序域(AppDomain)访问支持
  3. CodeDOM(代码文档对象模型)

我大概介绍一下后面两个,反射我就不说什么了,想必诸位都清楚,文章最后我会展示一下我做的一个Demo,主要有两个功能,动态编译代码和仿aspx页面生成html,功能很简单做工也很粗糙,但大概的原理过程都有,提供原程序下载,源程序注释都很详细,下下来就能看懂。

AppDomain(应用程序域):

先引用MSDN上的一句话:应用程序域(由 AppDomain 对象表示)为执行托管代码提供隔离、卸载和安全边界。是个什么意思呢?说得概括直白一点,就是它相当于一个容器,存放一些数据和其他资源,每个AppDomain都有自己的范围,各管各的事情,我们在将源代码(Source Code)动态编译成程序集后,还需要把它加载,最后运行,不然的话,动态编译就没什么意义了,想必各位清楚,在咱们学习“反射”的时候就知道用Assembly.Load()类似这样的方法加载程序集,然后创建实例(Instance),然后取得它的Type进行操作等等,但是如果按以上这样的方法加载的程序集的话,它默认被加载到当前AppDomain中(此当前AppDomain就是指程序一开始运行系统自动为其创建的一个AppDomain),又由于程序集不能单独卸载,也就是说加载进来之后的程序集会一直存在该AppDomain中,你如果加载的次数不多,这也就无所谓,但是如果像我们今天遇到的情况,需要反复不停的编译源代码,将生成的程序集加载运行的话,问题就出现了,最后加载进来的程序集就会越来越多,最后内存不足(Leak Memory),解决的办法就是,将每次用完的程序集卸载掉,目前卸载程序集的办法只有(我了解到的)卸载程序集所在的AppDomain,容器玩完了,容器里面的东西肯定也玩完了,但程序集如果一开始就加载到当前AppDomain中的话,问题就来了,因为当前AppDomain是不能被卸载的(如果当前AppDomain卸载了,什么都玩完了,怎么会有后续),所以,一开始加载程序集的时候,就不应该将其加载到当前AppDomain,这就需要我们新创建一个AppDomain,专门用来存放加载进来的程序集,一旦使用完毕后,马上将其卸载。创建新的AppDomain、跨域访问等具体操作,请看源代码。

CodeDOM(代码文档对象模型):

如果诸位知道DOM的话,这个其实很好理解,它两意思差不多,我们在处理html代码(XML)的时候,将它的各个节点都当做一个“对象”(Object),<Body></Body>、<Title></Title>等等,都是对象,在构建一个html文档的时候,我们可以先创建根,然后创建它的各个子节点,再然后设置各个子节点的属性、文本什么的,打个比方:HtmlHTML html = new HtmlHTML();HtmlHEAD head = new HtmlHead(); html.Childs.Add(head);创建两个节点对象,将一个加入到另外一个的子节点中去。.Net中System.Xml命名空间中有很多操作XML的类型,比如XmlAttribute、XmlElement、XmlComment等等,每一个类都代表XML文档中的一个节点元素,构建一个xml文档的时候,也是将一个加到另外一个的子节点集合中去,然后设置各个元素的其他属性等等,大概操作基本都这样。网上摘抄一段代码如下:

View Code
 1 XmlText xmltext; 
 2          XmlDocument xmldoc = new XmlDocument(); 
 3 
 4         //加入XML的声明段落 
 5          XmlNode xmlnode = xmldoc.CreateXmlDeclaration("1.0", "gb2312", null); 
 6          xmldoc.AppendChild(xmlnode); 
 7 
 8         //加入一个根元素 
 9          XmlElement xmlelem = xmldoc.CreateElement("", "bookstore", ""); 
10          xmltext = xmldoc.CreateTextNode(""); 
11          xmlelem.AppendChild(xmltext); 
12          xmldoc.AppendChild(xmlelem); 
13 
14         //加入一个子元素 
15          XmlElement xmlelem1 = xmldoc.CreateElement("", "book", ""); 
16          xmltext = xmldoc.CreateTextNode(""); 
17          xmlelem1.AppendChild(xmltext); 
18         //为子元素"book"增加两个属性 
19          xmlelem1.SetAttribute("genre", "", "fantasy"); 
20          xmlelem1.SetAttribute("ISBN", "2-3631-4"); 
21          xmldoc.ChildNodes.Item(1).AppendChild(xmlelem1);

如上所述,构建一个xml文档的代码。那么,CodeDOM就可以类比了,把什么命名空间(namespace)、类(Class)、引用(using)、方法(Method)、事件(Event)、赋值语句(AssignStatement)、异常捕获(TryCatchFinallyStatement)等等都当做对象,如果你仔细观察,发现其实程序源代码(Source Code)的结构跟html、xml等都是一样一样的,一层嵌套着另外一层,既然一样,那么我们构造一个源程序文档的方法肯定也是和构造DOM结构文档一样。

另外,像XmlAttribute、XmlComment、XmlNode这样的类型存在System.Xml命名空间中,那么CodeDOM中的各个类型存在哪儿呢?.Net中为我们提供的这些类型在System.CodeDOM这个命名空间中,基本上源程序中各个结构都能在这个命名空间中找到对应的类型。

到此,我们知道了利用System.CodeDOM中的类型动态的构造源代码,那么怎么将构造完的源代码保存下来(生成文本格式的代码,保存成源文件)?以及怎么将其编译,生成最终可以运行的程序集?.Net中又为我们提供了专门用于生成源代码和编译源代码的类,它们存在System.CodeDOM.Compiler命名空间中,具体要使用其中的两个接口ICodeCompiler和ICodeGenerator,它们两个的C#和VB版的具体实现类存在Microsoft.CSharp命名空间和Microsoft.VisualBasic命名空间。生成源代码和编译源代码操作都会返回执行结果,可以在返回结果中查看相关信息,比如编译源代码时,可以查看编译错误。

现在我们已经清楚了整个流程,来张图说明一下:

.Net开发笔记(八) 动态编译_第1张图片

图1

做的一个Demo

做的这个Demo中其实并没有使用到上图中的第2步和第3步,因为不需要动态生成源码,取而代之的是让用户直接输入源码,然后将输入的源码拿来编译执行,或者先将aspx页面代码(html和C#的混合体)解析成代码片段,然后拿来编译执行。也就是说,Demo中的流程应该是这样的:

.Net开发笔记(八) 动态编译_第2张图片

图2

源码注释很详细,我就在这不贴了。再来两张效果图:

.Net开发笔记(八) 动态编译_第3张图片

图3

.Net开发笔记(八) 动态编译_第4张图片

图4

.Net开发笔记(八) 动态编译_第5张图片

图5

源码C#部分XP .net3.5测试通过,VB部分没测试。

下载地址:http://download.csdn.net/detail/xiaozhi_5638/5172507

希望有帮助O(∩_∩)O~,如果觉得本文对您有用,请点击一下右下方的“赞”或者给个评论建议,非常感谢!!!

题外话:

    昨晚上跟b哥谈论一件衣服,一起回忆起了我大学时期的着装,无袖T恤+长短裤+ 人字拖,现在想想,甚是现世,不知不觉惭愧起来,责备自己年少不懂事,乱了校 规伤了风化。高三时,穿过一段时间的木屐+大裤衩,不知wy君是否还记得起?自认为学生时代没有给老师留下好的印象,学习成绩即不很突出,也不是老师眼中的乖乖娃,高中上语文时看物理,上物理课时看英语,被老师发现无数次,没办法,后来我一跟别人说我是偏科生,问哪科,答曰数学,无比脸红,丢尽了无数理科生的 脸,后来高考马马虎虎,读了一个二流大学,大一规规矩矩,大二开始翘课抄作业 ,即影响校容也丢了现代青年应有的颜面,谈来惭愧,不提也罢。有时候在讲一个 人过去是怎么“作恶多端”,并不是想衬托TA现在有多大进步,或者炫耀TA有一个 多么“拉风”的青春,这就像现在许多人讲自己出生如何贫寒,起跑线比别人多么 靠后,条件如何艰苦,但TA还是一直会坚持下去,他相信自己是个潜力股,说到这,无比骄傲,其实殊不知,出生贫寒并不是优势,起跑线落后更不是资本, 你说你是“潜力股”,吁,“潜力股”好多时候也许只是安慰那些有着弱小心灵的屌丝,不是吗,跑题了。其实我本人倒是一直推崇做事做完了就完了,不要总是回过头 去想它对不对,更要避免耿耿于怀,但学生时代的所作所为,有时候的确让我不安 ,觉得愧对各位老师,也许他们根本记不起,但还是在此道个不是,虽然我现在并 不是老师,也没有身兼“传道授业解惑者”这样的职业。《单车》中有句歌词“经 已给我怎会看不到,虽说演你角色实在有难度”,虽然角色不同,意义大概一样吧 。

你可能感兴趣的:(.Net开发笔记(八) 动态编译)