代碼生成器與 .NET
Pierre Couzy
WinWise
摘要:代碼生成器是您日常生活中的一部分,即使您沒有意識到這一點。Pierre Couzy 說明了如何在項目中利用它們。
本頁內容
簡介 | |
工具和示例 | |
版本控制 | |
小結 |
簡介
假設您在一家由 DBA 統治一切的公司裏工作:您不能生成只是“到 Oracle 那裏去取一些記錄”的應用程序。您只能依靠存儲過程,因爲在該級別存在安全層。
生成應用程序通常涉及到下列步驟:
1. |
創建一批存儲過程。 |
2. |
創建能夠與存儲過程通信的 C# 類。 |
3. |
創建將管理 Microsoft ASP.NET 表單或 Microsoft Windows 窗體或者形成另一個層的更高級別的類。 |
步驟 1 和步驟 2 是密切相關的:它們共享大量結構,它們消耗另一個步驟所產生的東西,等等。問題在於,您擁有兩種不同的語言(在該示例中,爲 PL-SQL 和 C#),並且無法從其中一種語言引用另一種語言。
您說這沒有什麼大不了的?團隊中的第一個開發人員將使一切保持正常,因爲他了解 .NET、PL/SQL 以及安全模型的本質。他可能將編寫兩個東西:首先是一組幫助器類,然後是一個有效的示例 — 於是,其他開發人員將使用這些幫助器類,複製/粘貼有效示例,並針對他們的需要進行修改。
如果您已經在軟件行業中工作了足夠長的時間,就會知道接下來將會發生什麼事情:隨着要求的增加,幫助器類將慢慢變大,並且其中一些將很快過時。當然,沒有人敢於修改它們,因爲這可能會毀壞較舊的項目。同時,開發人員將重用不再與新規則同步的有效示例,並且他們將沒有辦法瞭解哪些部分仍在使用,以及哪些部分可以毫無風險地丟棄。您將陷入一片混亂之中(複製/粘貼編程)。
有很多克服這一複雜性的辦法,而我將介紹您可能已經忽視的一種有用的辦法 — 代碼生成器。其實您已經使用了它們,即使您沒有意識到這一點:每當您創建一個新的 ASPX 頁時,隱藏機制就會將它轉換爲 C# 或 Microsoft Visual Basic 類。當您在項目中引用 COM 組件、Web 服務甚至數據結構(類型化數據集)時,會發生相同的事情 — 有一個類被自動生成,它隱藏了您不希望瞭解的複雜性。
工具和示例
您將遇到的第一批代碼生成器只是一些隱藏複雜性的工具。它們不允許您解釋如何生成代碼,並且不讓您隨後更改生成的代碼(它們將清除您進行的更新)。
例如,“Add Web Reference Wizard”甚至不向您顯示插入到項目中的代碼(除非您請求它顯示)。
圖 1. 顯示爲 Web 引用生成的代碼
Windows 窗體設計器也是一個代碼生成器。在這裏,您可以查看代碼,但最好不要動它。
#region Windows Form Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { this.button1 = new System.Windows.Forms.Button(); this.SuspendLayout();
讓我們更進一步地探討該問題:代碼生成器可以提供格式規範的主幹,以供您隨後添加自己的代碼。您可能知道 Reflector,但您是否知道 Reflector 的一些外接程序 (http://www.dotnetwiki.org/Default.aspx?tabid=52) 包含代碼生成器?它們使您可以執行以下工作:
圖 2. 使用 Reflector 生成代碼
當您按下“Generate”按鈕時,將創建一個新文件:
// Generated by Refly namespace MyTestNameSpace { using System; /// <summary>Test fixture for the <see cref="SomeBusinessClass"/> class ///</summary> /// <remarks /> [TestFixture()] public class SomeBusinessClassTest { /// <summary /> /// <remarks /> private SomeBusinessClass _someBusinessClass = null; /// <summary /> /// <remarks /> public virtual SomeBusinessClass SomeBusinessClass { get { return this._someBusinessClass; } } /// <summary>Tests the EstimateSomeNumber method</summary> /// <remarks> /// <para>Test coverage (estimated): 100,0%</para> /// <para>Target path:</para> /// <code>/* 0 */ return 0x2a; /// </code> /// </remarks> [Test()] [Ignore()] public virtual void EstimateSomeNumber0() { throw new System.NotImplementedException(); } /// <summary>Tests the IsSomethingAlreadyInDataBase method</summary> /// <remarks> /// <para>Test coverage (estimated): 100,0%</para> /// <para>Target path:</para> /// <code>/* 0 */ return false; /// </code> /// </remarks> [Test()] [Ignore()] public virtual void IsSomethingAlreadyInDataBase0() { throw new System.NotImplementedException(); } /// <summary>Sets up the fixture</summary> /// <remarks /> [SetUp()] public virtual void SetUp() { throw new System.NotImplementedException(); } /// <summary>Releases resource allocated in the fixture</summary> /// <remarks /> [TearDown()] public virtual void TearDown() { throw new System.NotImplementedException(); } } }
您不瞭解有關“單元測試”的任何內容,但是您仍然能夠生成有效的測試類。與上一個示例的不同之處在於,這一次您必須在生成的類的內部編碼。可是,我們仍然無法創建我們自己的模板。
下一個步驟是創建我們自己的代碼生成器。我們需要的全部東西就是一種根據一組常用信息(例如,將要獲得的列的名稱、要用來請求信息的方式、要允許的事務種類等等)來生成文本文件(C#、PL-SQL 等等)的方式。當然,您無法生成所有東西,因此需要一個能夠生成自定義主幹的工具,而某個開發人員隨後將添加特定的實現細節。
該工具可以是 Perl、Microsoft VBScript 或普通的舊式 ASP(就本文而言)— 畢竟,它是一種可以根據參數生成文本文件的很好的工具。您可能使用 ASP 生成 HTML 文件,但是它還適用於生成 Microsoft Excel 或 CSV、WML — 因此,爲什麼不用它來生成 T-SQL 或 C# 呢?
您的 ASP 文件可能如下所示:
<%@ Language=VBScript %> <%if request("Generate").count = 0 then%> <HTML><BODY> <form method=post> <P>Property name :<INPUT name=PropertyName></P> <P>What is the type of your property ? <INPUT name=PropertyType></P> <P>Read only <input type=checkbox name=ReadOnly value=true></P> <P><INPUT type=submit value="Generate !" name=Generate></P> </form> </BODY></HTML> <% else dim PropertyName, PropertyType, ReadOnly, PropertyModifier PropertyName = Request("PropertyName") PropertyType = Request("PropertyType") ReadOnly = (Request("ReadOnly")="true") if ReadOnly then PropertyModifier = "ReadOnly" else PropertyModifier = "" Response.ContentType = "text/plain" %> Private _<%=PropertyName%> as <%=PropertyType%> Public <%=propertyModifier%> Property <%=PropertyName%> as <%=PropertyType%> Get Return _<%=PropertyName%> End Get <%if not ReadOnly then%> Set (ByVal Value as <%=PropertyType%>) _<%=PropertyName%> = Value End Set <%end if%> End Property <%end if%>
當您執行該文件時,您將獲得如下所示的內容:
圖 3. 生成的 Web 頁
圖 4. 生成的代碼
您還可以在 Internet 上找到代碼生成器。我通常使用 CodeSmith;它是免費的並且易於理解,但是您可以找到很多其他的代碼生成器。圖 5 是一個屏幕截圖,它顯示了一種等待泛型的聰明方式;它生成一個強類型的哈希表。
圖 5. CodeSmith
這些工具通常接受兩個輸入 — 一個模板文件(像我們的 ASP 文件)和一個 XML 參數文件(像表單的內容),併產生一個新文件。
開發人員被賦予這些模板,並且他們通常通過同一組參數來使用兩個不同的模板。這樣,他們將相同的信息給予這兩個模板,並且獲得共享信息的一個 T-SQL 文件和一個 C# 文件,同時無需承擔遺忘或鍵入錯誤的風險;這樣就爲您完成了此工作的乏味部分。
版本控制
當然,在使用模板和自動生成的代碼時,事情會變得複雜。在現實生活中,模板會演化(例如,您可能希望賦予 C# 類一種調用某些存儲過程的異步方式);如果是這樣,那麼您需要一種相應的辦法,以應付開發人員在主幹生成之後放入的實現。
爲此,您必須確保開發人員添加的代碼獨立於您的模板所生成的代碼。實現此目的最簡單的技術是創建兩個不同的類:一個用於生成的代碼,另一個從第一個繼承,但將不生成。
圖 6. 對生成的代碼進行版本控制
由於該技術所具有的繼承機制,因此它在您生成 .NET 代碼時非常適用;但是,如果您要生成其他種類的代碼(VBScript、SQL),則將無法以這種方式工作。在那種情況下,您主要依靠自己來解決問題。我使用的機制很簡單:我在生成的代碼中提供了佔位符(在 C# 中爲區域;在 SQL 中爲特定註釋所圍繞的塊),並且當代碼被重新生成時,只有這些區域中的代碼被保留。傳輸自定義代碼本身同樣簡單:模板始終接受 PreviousFile 參數。當該參數存在時,將分析以前的文件以獲得自定義代碼,並將其重新插入到當前的生成文件中。
如果沒有辦法恢復原樣,則千萬不要弄亂生成的代碼:您將發現自己正在進行復制/粘貼編程。
小結
下面是在使用代碼生成器時需要記住的一些事情:
• |
如果不打算對生成的代碼進行修改,請預先聲明。 |
• |
不要忘記,開發人員沒有時間瞭解生成代碼的內部結構,因此請爲他們提供幫助,並且儘可能詳細地對入口點進行說明。 |
• |
考慮進行版本控制,並明確說明您將允許人們做什麼。或許您希望開發人員只在代碼的特定區域中添加自定義代碼,或者您希望他們從您的類繼承。 |
• |
模板的新版本應當始終能夠重新生成舊版本所生成的代碼。如果新版本需要比舊版本更多的代碼以便正確工作,則請將需要代碼的位置變得明顯一些,並且插入一些能夠生成錯誤的代碼(在編譯時 — 如果可能的話)。 |
• |
如果您決定中斷模板的版本控制機制(例如,通過添加在應用新版本時無法恢復原樣的代碼),請保留該模板和您使用的參數的副本。如果您需要爲某個舊項目生成另一個類似的代碼文件,並且您已經丟失了在開發該舊項目時使用的模板版本,則會發生很糟糕的事情。我只是將模板和參數與項目一起放在 SourceSafe 中。 |
• |
在發佈模板之前,對其進行徹底的測試。模板會在開發人員之間飛快地傳播,因爲他們無需多少知識就可以解決問題。如果更新生成的文件意味着丟失自定義,那麼程序錯誤將很難查找,並且更加難以修復。 |
使用該方法能夠走多遠?簡單說來,代碼生成器使開發人員可以獲得一個抽象級別。他不必將精力集中於技術過程(因爲該過程已嵌入到模板中),並且可以考慮集成該過程。它使開發人員能夠更好地理解客戶需求,並使技術過程擁有更好的質量。Microsoft Visual Studio 已經廣泛使用了這些技術,並且即將問世的版本已經添加了很多改進(引人注目的是一個類建模程序和不完整類機制,可用於將生成的代碼與自定義代碼分隔開)。
現實世界中的 .NET
Pierre Couzy 是一個專門研究分佈式系統體系結構的培訓師和顧問。作爲 20 餘部書籍的作者,他目前被 WinWise 聘爲 ASP.NET 和 BizTalk 專家。他自 2003 年以來一直是地區主管,並且在法國的許多重要會議上發表了演講。如果您要與他討論法國爵士樂、橋牌(是的,就是那種簡單的遊戲)甚至計算機,請通過 [email protected] 與他聯繫。