使用 T4 文字範本在設計階段產生程式碼
設計階段 T4 文字範本可讓您在 Visual Studio 專案中產生程式碼和其他檔案。 通常您會撰寫範本,讓範本可以改變依據「模型」(Model) 的資料而產生的程式碼。 模式是一種檔案或資料庫,其中包含應用程式需求的重要資訊。
例如,您可能擁有將工作流程定義為資料表或圖表的模型。 從這個模型,即可產生執行該工作流程的軟體。 當使用者的需求改變時,可輕鬆與使用者討論新的工作流程。 從工作流程重新產生程式碼比手動更新程式碼更為可靠。
注意事項 |
---|
「模型」(Model) 是描述應用程式特定層面的資料來源。它可以是以任何檔案或資料庫類型為形式的任何格式。不一定非得是特定格式,例如 UML 模型或網域指定的語言模型。模型的格式一般為資料表或 XML 檔案。 |
您可能已經熟悉程式碼產生作業。 當您在 Visual Studio 方案的 .resx 檔中定義資源時,將會自動產生一組類別和方法。 資源檔讓編輯資源的工作變得比編輯類別和方法更輕鬆也更可靠。 有了文字範本,您就可以用相同的方式從自己設計的原始檔產生程式碼。
文字範本中混合了要產生的文字和會產生文字可變部分的程式碼。 程式碼可讓您重複或依條件省略產生之文字的組成部分。 產生的文字本身可以是會構成應用程式之一部分的程式碼。
建立設計階段 T4 文字範本
若要在 Visual Studio 中建立設計階段 T4 範本
建立 Visual Studio 專案或開啟現有專案。
例如,在 [檔案] 功能表上,選擇 [新增], [Project]。
加入文字範本檔加入至專案並將其副檔名為 .tt 的名稱。
若要這麼做,請在 [方案總管],在專案的捷徑功能表,選擇[新增], [新項目]。 在 [新增項目] 對話方塊中選擇 [文字範本] 從中間窗格。
請注意,該檔案的 [自訂工具] 屬性是 [TextTemplatingFileGenerator]。
開啟檔案。 其中已經包含下列指示詞:
<#@ template hostspecific="false" language="C#" #> <#@ output extension=".txt" #>
如果您已將範本加入至 Visual Basic 專案,則語言屬性將會是 "VB"。
在檔案結尾加入一些文字。 例如:
Hello, world!
儲存檔案。
您可能會看到 [安全性警告] 訊息方塊,要求確認是否要執行範本。 按一下 [確定]。
在 [方案總管],展開範本檔案節點,您會發現有 .txt 副檔名的檔案。 檔案包含從範本產生的文字。
注意事項 如果您的專案是 Visual Basic 專案中,您必須按一下 [顯示所有檔案] 才能看見輸出檔。
重新產生程式碼
在下列任一情況中,將會執行範本並產生附屬檔案:
編輯樣板接著將焦點變更至不同的 Visual Studio 視窗。
儲存範本。
在 [組建] 功能表的[轉換所有範本] 。 如此隨即轉換 Visual Studio 方案中的所有範本。
在 [方案總管],在所有檔案捷徑功能表,選取 [執行自訂工具]。 使用這個方法會將範本的替代的子集。
您也可以設定 Visual Studio 專案,以便在範本所讀取的檔案變更時執行範本。 如需詳細資訊,請參閱自動重新產生程式碼。
產生變數文字
文字範本可讓您使用程式碼來變更產生之程式碼的內容。
若要使用程式碼來產生文字
變更 .tt 檔案的內容:
<#@ template hostspecific="false" language="C#" #> <#@ output extension=".txt" #> <#int top = 10; for (int i = 0; i<=top; i++) { #> The square of <#= i #> is <#= i*i #> <# } #>
<#@ template hostspecific="false" language="VB" #> <#@ output extension=".txt" #> <#Dim top As Integer = 10 For i As Integer = 0 To top #> The square of <#= i #> is <#= i*i #> <# Next #>
儲存 .tt 檔,並再次檢查產生的 .txt 檔。 它會列出數字 0 到 9 的平方值。
請注意,陳述式是置於 <#...#> 內,而單一運算式則是置於 <#=...#> 內。 如需詳細資訊,請參閱撰寫 T4 文字範本。
如果要在 Visual Basic 撰寫生成程式碼 (Generating Code),template 指示詞必須包含 language="VB"。 "C#" 為預設值。
偵錯設計階段 T4 文字範本
偵錯文字範本:
將 debug="true" 插入 template 指示詞。 例如:
<#@ template debug="true" hostspecific="false" language="C#" #>
將範本,方式與您為一般程式碼會的中斷點。
從文字範本檔的捷徑功能表在方案總管中選取 [偵錯 T4 範本] 。
範本會執行並在中斷點停止。 您可以透過程式碼檢查變數和步驟以一般方式。
提示
debug="true" 更精確地產生的程式碼對應至文字範本,透過插入詳細行號指示詞到產生的程式碼。如果您忽略它,中斷點可能會停止在錯誤狀態中執行。
即使您沒有進行偵錯,不過,您可以在範本指示詞中的子句)。這會導致效能的只非常小置放。
產生方案的程式碼或資源
您可以根據模型產生不同的程式檔案。 模型是像資料庫、組態檔、UML 模型、DSL 模型或其他來源之類的輸入。 您通常可以從相同的模型產生許多程式檔。 為了達到這個目的,您要為每個產生的程式檔建立範本檔,並讓所有的範本都讀取相同的模型。
若要產生程式碼或資源
變更輸出指示詞以產生適當類型的檔案,例如 .cs、.vb、.resx 或 .xml 檔。
插入將會產生所需方案程式碼的程式碼。 例如,如果您要在類別中產生三個整數欄位宣告,就插入下列程式碼:
<#@ template debug="false" hostspecific="false" language="C#" #> <#@ output extension=".cs" #> <# var properties = new string [] {"P1", "P2", "P3"}; #> class MyGeneratedClass { <# foreach (string propertyName in properties) { #> private int <#= propertyName #> = 0; <# } #> }
<#@ template debug="false" hostspecific="false" language="VB" #> <#@ output extension=".cs" #> <# Dim properties = {"P1", "P2", "P3"} #> class MyGeneratedClass { <# For Each propertyName As String In properties #> private int <#= propertyName #> = 0; <# Next #> }
儲存檔案並檢查產生的檔案,這個檔案現在包含了下列程式碼:
class MyGeneratedClass { private int P1 = 0; private int P2 = 0; private int P3 = 0; }
生成程式碼和產生的文字
產生程式碼時,最重要的是要避免讓在範本中執行之生成程式碼 (Generating Code) 與結果成為方案之一部分的產生程式碼 (Generated Code) 混淆不清。 這兩個程式碼的語言不一定要相同。
上一個範例有兩個版本。 在其中一個版本中,產生的程式碼語言為 C#。 在另一個版本中,產生的程式碼語言為 Visual Basic。 但是這兩者產生的文字相同,屬於 C# 類別。
您可以用相同的方式,使用 Visual C# 範本來產生以任何語言撰寫的程式碼。 產生的文字不一定非得是特定語言,也不一定非得是程式的程式碼。
建構文字範本
依照良好的慣例,我們傾向於將範本程式碼分隔成兩部分:
組態或資料收集部分,會在變數中設定值,但不包含文字區塊。 在上一個範例中,這部分是 properties 的初始設定。
有時候這稱為 [模型] 區段,因為它會建構一個存放區內的模式,而且通常會讀取模型檔案。
文字產生部分 (在範例中為 foreach(...){...}),會使用變數的值。
這部分不是必要的分隔,但是為一種風格 -- 透過降低文字部分的複雜度,提高範本的可讀性。
讀取檔案或其他原始檔
若要存取模型檔或資料庫,範本程式碼可以使用 System.XML 這類組件。 若要存取這些組件,則必須插入如下所示的指示詞:
<#@ assembly name="System.Xml.dll" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.IO" #>
assembly 指示詞可讓指定的組件能夠使用範本程式碼,其方式就如同 Visual Studio 專案中的 [參考] 區段。 您不必包含 System.dll 的參考,這個組件會被自動參考。 import 指示詞讓您不必使用完整名稱就可以使用型別,其使用方式如同一般程式檔中的 using 指示詞。
例如,匯入 System.IO 之後,您可以撰寫:
<# var properties = File.ReadLines("C:\\propertyList.txt");#>
...
<# foreach (string propertyName in properties) { #>
...
<# For Each propertyName As String In
File.ReadLines("C:\\propertyList.txt")
#>
使用相對路徑名稱開啟檔案
若要從文字範本的相對位置載入檔案,請使用 this.Host.ResolvePath()。 若要使用 this.Host,您必須在 template 中設定 hostspecific="true":
<#@ template debug="false" hostspecific="true" language="C#" #>
然後您可以撰寫如下範例內容:
<# string fileName = this.Host.ResolvePath("filename.txt");
string [] properties = File.ReadLines(filename);
#>
...
<# foreach (string propertyName in properties { #>
...
<# Dim fileName = Me.Host.ResolvePath("propertyList.txt")
Dim properties = File.ReadLines(filename)
#>
...
<# For Each propertyName As String In properties
...
#>
您也可以使用 this.Host.TemplateFile,它會識別目前範本檔的名稱。
this.Host (在 VB 中為 Me.Host) 的型別為 Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost。
從 Visual Studio 取得資料
若要使用提供的服務。 Visual Studio,請將 hostSpecific 屬性並載入 EnvDTE 組件。 您可以使用 IServiceProvider.GetCOMService() 存取 DTE 和其他服務。 例如:
<#@ template hostspecific="true" language="C#" #>
<#@ output extension=".txt" #>
<#@ assembly name="EnvDTE" #>
<#
IServiceProvider serviceProvider = (IServiceProvider)this.Host;
EnvDTE.DTE dte = (EnvDTE.DTE) serviceProvider.GetCOMService(typeof(EnvDTE.DTE));
#>
Number of projects in this VS solution: <#= dte.Solution.Projects.Count #>
提示
文字範本會在其應用程式網域中執行,因此,這項服務會透過封送處理存取。在這種情況下, GetCOMService() 比 GetService() 可靠。
自動重新產生程式碼
一般而言,會使用一個輸入模型產生 Visual Studio 方案中的數個檔案。 每個檔案各自從本身的範本產生,但這些範本都參考同一個模型。
如果來源模型變更,就必須重新執行方案中的所有範本。 若要手動執行這項操作,請選取在 [組建] 功能表的 [轉換所有範本] 。
如果您已安裝 Visual Studio Visualization and Modeling SDK,即可在每次建置時自動轉換所有範本。 若要這麼做,請以文字編輯器來編輯專案檔 (.csproj 或 .vbproj),並在檔案結尾附近的所有其他 <import> 陳述式之後加入下列幾行:
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v11.0\TextTemplating\Microsoft.TextTemplating.targets" />
<PropertyGroup>
<TransformOnBuild>true</TransformOnBuild>
<!-- Other properties can be inserted here -->
</PropertyGroup>
如需詳細資訊,請參閱建置流程中的程式碼產生。
錯誤報告
若要在 Visual Studio 錯誤視窗中放置錯誤和警告訊息,您可以使用下列方法:
Error("An error message");
Warning("A warning message");
將現有檔案轉換為範本
範本有個好用的功能,就是加上一些插入的程式碼之後,範本看起來會和它所產生的檔案非常相像。 這讓人想到建立範本的有用方法, 即首先建立一般檔案做為原型 (例如 Visual C# 檔案),然後逐步加入變更結果檔案的產生程式碼。
若要將現有檔案轉換設計階段範本
在 Visual Studio 專案中,加入要產生的檔案類型,例如 .cs、.vb 或 .resx 檔案。
測試新檔案以確定可以運作。
在 [方案總管] 中,將副檔名變更為 .tt。
確認 .tt 檔案的下列屬性:
自訂工具 =
TextTemplatingFileGenerator
建置動作 =
None
在檔案開頭插入下列幾行:
<#@ template debug="false" hostspecific="false" language="C#" #> <#@ output extension=".cs" #>
如果您想要以 Visual Basic 撰寫範本的生成程式碼,請將 language 屬性設為 "VB" 而非 "C#"。
將 extension 屬性設定為要產生之檔案類型的副檔名,例如 .cs、.resx 或 .xml。
儲存檔案。
具有指定之副檔名的附屬檔案隨即建立。 就檔案類型來看,其屬性是正確的。 例如,.cs 檔案的 [建置動作] 屬性將會是 [編譯]。
確認產生的檔案與原來的檔案包含相同內容。
找出您想要在檔案中變更的部分。 例如,僅在特定情況下出現的部分,或重複的部分,或是其特定值有所不同的部分。 插入生成程式碼。 儲存檔案,並確認已正確產生附屬檔案。 重複此步驟。
程式碼產生作業的方針
請參閱撰寫 T4 文字範本的方針。
後續步驟
後續步驟 |
主題 |
---|---|
透過使用輔助功能 (包括檔案和持續性資料) 的程式碼,撰寫和偵錯更進階的文字範本。 |
|
在執行階段從範本產生文件。 |
|
在 Visual Studio 外部執行文字產生作業。 |
|
將資料的形式轉換為 Domain-Specific Language。 |
|
撰寫指示詞處理器來轉換自己的資料來源。 |