雖然標題為“動態編譯”,其實本文包含兩個部分,一個就是標題說的動態編譯原始碼,另外一個應該是動態產生原始碼。也就是說,在.Net中可以存在這樣一種情況:在程式運行期間,由程式控制動態產生一份原始碼(Source Code),然後再編譯該原始碼,產生一個新的程式集,緊接著再載入產生的程式集,最後運行。這個情況可能出現在很多地方,比如一些模板代碼產生,像asp.net頁面產生html,還可以在程式運行期間,由使用者控制,動態擴充程式功能。
要瞭解以上的實現過程,需先瞭解以下幾個知識點:
- 反射
- 跨應用程式定義域(AppDomain)訪問支援
- CodeDOM(程式碼文件物件模型)
我大概介紹一下後面兩個,反射我就不說什麼了,想必諸位都清楚,文章最後我會展示一下我做的一個Demo,主要有兩個功能,動態編譯代碼和仿aspx頁面產生html,功能很簡單做工也很粗糙,但大概的原理過程都有,提供原程式下載,來源程式注釋都很詳細,下下來就能看懂。
AppDomain(應用程式定義域):
先引用MSDN上的一句話:應用程式定義域(由 AppDomain 對象表示)為執行Managed 程式碼提供隔離、卸載和安全邊界。是個什麼意思呢?說得概括直白一點,就是它相當於一個容器,存放一些資料和其他資源,每個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命名空間。產生原始碼和編譯原始碼操作都會返回執行結果,可以在返回結果中查看相關資訊,比如編譯原始碼時,可以查看編譯錯誤。
現在我們已經清楚了整個流程,來張圖說明一下:
圖1
做的一個Demo:
做的這個Demo中其實並沒有使用到中的第2步和第3步,因為不需要動態產生源碼,取而代之的是讓使用者直接輸入源碼,然後將輸入的源碼拿來編譯執行,或者先將aspx頁面代碼(html和C#的混合體)解析成程式碼片段,然後拿來編譯執行。也就是說,Demo中的流程應該是這樣的:
圖2
源碼注釋很詳細,我就在這不貼了。再來兩張:
圖3
圖4
圖5
源碼C#部分XP .net3.5測試通過,VB部分沒測試。
:http://download.csdn.net/detail/xiaozhi_5638/5172507
希望有協助O(∩_∩)O~,如果覺得本文對您有用,請點擊一下右下方的“贊”或者給個評論建議,非常感謝!!!
題外話:
昨晚上跟b哥談論一件衣服,一起回憶起了我大學時期的著裝,無袖T恤+長短褲+ 人字拖,現在想想,甚是現世,不知不覺慚愧起來,責備自己年少不懂事,亂了校 規傷了風化。高三時,穿過一段時間的木屐+大褲衩,不知wy君是否還記得起?自認為學生時代沒有給老師留下好的印象,學習成績即不很突出,也不是老師眼中的乖乖娃,高中上語文時看物理,上物理課時看英語,被老師發現無數次,沒辦法,後來我一跟別人說我是偏科生,問哪科,答曰數學,無比臉紅,丟盡了無數理科生的 臉,後來高考馬馬虎虎,讀了一個二流大學,大一規規矩矩,大二開始翹課抄作業 ,即影響校容也丟了現代青年應有的顏面,談來慚愧,不提也罷。有時候在講一個 人過去是怎麼“作惡多端”,並不是想襯托TA現在有多大進步,或者炫耀TA有一個 多麼“拉風”的青春,這就像現在許多人講自己出生如何貧寒,起跑線比別人多麼 靠後,條件如何艱苦,但TA還是一直會堅持下去,他相信自己是個潛力股,說到這,無比驕傲,其實殊不知,出生貧寒並不是優勢,起跑線落後更不是資本, 你說你是“潛力股”,籲,“潛力股”好多時候也許只是安慰那些有著弱小心靈的屌絲,不是嗎,跑題了。其實我本人倒是一直推崇做事做完了就完了,不要總是回過頭 去想它對不對,更要避免耿耿於懷,但學生時代的所作所為,有時候的確讓我不安 ,覺得愧對各位老師,也許他們根本記不起,但還是在此道個不是,雖然我現在並 不是老師,也沒有身兼“傳道授業解惑者”這樣的職業。《單車》中有句歌詞“經 已給我怎會看不到,雖說演你角色實在有難度”,雖然角色不同,意義大概一樣吧 。