首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

Visual Studio DSL 入門 13:結合 T4 範本生成程式碼

     編輯:關於.NET

前言

前幾節已完成狀態機設計器,可視覺化設計狀態流程。但若止於「畫圖」,不如直接用 UML 工具。模型驅動開發 (MDD) 的核心價值在於從模型自動生成程式碼。本節示範結合 T4 (Text Template Transformation Toolkit) 實現程式碼生成。

DSL 結合程式碼生成的常見方式

  • 硬式編碼:在程式中直接遍歷模型拼接字串 (如 ActiveWriter 生成 NHibernate 程式碼)。
  • 範本引擎:XSLT、T4、CodeSmith 等。VS 內建 T4 支援 (DSL、LINQ to SQL 皆用 T4 生成),本節採用 T4。

VS 未內建 T4 編輯器,建議先安裝免費的 T4 Editor Community Edition 獲得語法醒目提示。

1. 觀察預設產生的 T4 範例

啟動專案,Debugging 專案下有 LanguageSmReport.tt:

<#@ Import Namespace="System" #>
<#@ template inherits="Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation" language="C#" #>
<#@ output extension=".txt" #>
<#@ LanguageSm processor="LanguageSmDirectiveProcessor" requires="fileName='Test.mydsl5'" #>

<#
foreach (State state in StateMachine.States) {
#>
<#= state.Name #>
<#
}
#>

執行「執行自訂工具」產生 LanguageSmReport.txt:

Draft
NewOrder
Cancelled
Confirmed

對應的模型圖:

訂單狀態機模型圖

2. T4 範本結構解析

  • <#@ Import ... #>:引用命名空間,對應 DLL 需加入專案參考。
  • <#@ template inherits="..." language="C#" #>:指定基類 ModelingTextTransformation,語言可用 C#3.5 以支援 .NET 3.5 語法。
  • <#@ output extension=".txt" #>:輸出副檔名與編碼。
  • <#@ LanguageSm processor="..." requires="..." #>:宣告指令處理器與要載入的模型檔。
  • 控制區塊:
    • <# ... #>:標準控制區塊,放 C#/VB 控制陳述式。
    • <#+ ... #>:類別特性區塊,放可重用的方法、屬性、巢狀類別。
    • <#= ... #>:運算式區塊,計算值並輸出。

3. T4 執行機制:生成的轉換類別

T4 類似 ASPX,會編譯成衍生自 ModelingTextTransformation 的類別,TransformText() 透過 Write() 輸出內容,Initialize() 載入模型 (透過 SerializationHelper.LoadModel)。

4. 實戰目標:從狀態機生成訂單狀態列舉與狀態轉換方法

根據模型圖,欲生成程式碼結構:

public enum OrderStateEnum { Draft, NewOrder, Cancelled, Confirmed }

public partial class Order
{
    public OrderStateEnum OrderState { get; set; }
    public Order() {}
    public void Save(Order order) { /* 存檔邏輯 */ }

    // 每個 Transition 產生一個方法
    protected void SaveOrder(Order order) {
        if (order.OrderState == OrderStateEnum.Draft)
            order.OrderState = OrderStateEnum.NewOrder;
        Save(order);
    }
    protected void CancelOrder(Order order) { ... }
    protected void ConfirmOrder(Order order) { ... }
}
  • 所有 State 產生列舉值。
  • 固定屬性 OrderState。
  • 每個 Transition 產生一個方法:方法名 = Transition.Event,本體判斷目前狀態為 Predecessor 則轉為 Successor。

5. 撰寫 T4 範本

步驟 1:加入輔助方法取得所有 Transition (置於範本最底部,類別特性區塊)

<#+
System.Collections.Generic.IEnumerable<Transition> GetAllTransitions() {
    foreach (State s in StateMachine.States)
        foreach (Transition t in Transition.GetLinksToSuccessors(s))
            yield return t;
}
#>

步驟 2:主範本內容

/// <summary>
/// 訂單狀態
/// </summary>
public enum OrderStateEnum
{
<#
    PushIndent(" ");
    foreach (State state in StateMachine.States)
        WriteLine(" {0},", state.Name);
    PopIndent();
#>
}
/// <summary>
/// 訂單生成
/// </summary>
public partial class Order
{
    public OrderStateEnum OrderState { get; set; }
    public Order() {}
    public void Save(Order order){ /* to save order */ }

<#
    foreach (Transition transition in GetAllTransitions()) {
#>

    protected void <#= transition.Event #>(Order order)
    {
        if (order.OrderState == OrderStateEnum.<#= transition.Predecessor.Name #>)
            order.OrderState = OrderStateEnum.<#= transition.Successor.Name #>;
        Save(order);
    }
<#
    }
#>
}

6. 轉換範本產生程式碼

執行「轉換所有範本」或右鍵 T4 檔案「執行自訂工具」,產生完整程式碼:

/// <summary>
/// 訂單狀態
/// </summary>
public enum OrderStateEnum
{
    Draft,
    NewOrder,
    Cancelled,
    Confirmed,
}
/// <summary>
/// 訂單生成
/// </summary>
public partial class Order
{
    public OrderStateEnum OrderState { get; set; }
    public Order() {}
    public void Save(Order order){ /* to save order */ }

    protected void SaveOrder(Order order)
    {
        if (order.OrderState == OrderStateEnum.Draft)
            order.OrderState = OrderStateEnum.NewOrder;
        Save(order);
    }
    protected void CancelOrder(Order order)
    {
        if (order.OrderState == OrderStateEnum.NewOrder)
            order.OrderState = OrderStateEnum.Cancelled;
        Save(order);
    }
    protected void ConfirmOrder(Order order)
    {
        if (order.OrderState == OrderStateEnum.NewOrder)
            order.OrderState = OrderStateEnum.Confirmed;
        Save(order);
    }
}

7. 重要觀念:T4 生成與手寫程式碼的共存策略

T4 無法在生成檔中保留手寫自訂邏輯 (每次重新生成會覆蓋)。實務上採用 partial class 分檔策略:

  • T4 生成檔:放置固定、可從模型推導的程式碼 (列舉、屬性、標準轉換方法)——嚴禁手動修改。
  • 手寫擴充檔:同一個 partial 類別的另一個 .cs 檔,放置業務邏輯、自訂驗證、複雜演算法等——安全保留。

如此模型修改後重新生成,不會覆蓋手寫邏輯。

程式碼下載

LanguageSm5.rar

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved