代碼生成器與 .NET (from msdn)

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 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] 與他聯繫。

轉到原英文頁面


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章