http://www.microsoft.com/china/msdn/library/langtool/vsdotnet/realworld11022004.mspx
代碼產生器與 .NET發布日期: 12/23/2004 | 更新日期: 12/23/2004
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 Reflynamespace 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, PropertyModifierPropertyName = 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 年以來一直是地區主管,並且在法國的許多重要會議上發表了演講。如果您要與他討論法國爵士、橋牌(是的,就是那種簡單的遊戲)甚至電腦,請通過 Pierre.couzy@winwise.fr 與他聯絡。