编写 T4 文本模板
更新:2011 年 3 月
文本模板包含将从其生成的文本。 例如,用于创建网页的模板包含“<html>…”和 HTML 页的所有其他标准部件。 插入模板中的是控制块,控制块是程序代码片断。 控制块提供变化值,允许文本部件是条件和重复的。
使用这一结构很容易开发模板,因为可以以生成文件为原型,然后逐步插入用于改变结果的控制块。
文本模板由以下部件组成:
-
指令 - 控制模板处理方式的元素。
-
文本块 - 直接复制到输出的内容。
-
控制块 - 向文本插入可变值并控制文本的条件或重复部件的程序代码。
若要尝试本主题中的示例,请将它们复制到模板文件中,如使用 T4 文本模板生成设计时代码所述。 编辑模板文件后,保存该文件,然后检查输出 .txt 文件。
控制块是用于转换模板的程序代码节。 默认语言是 C#,但若要使用 Visual Basic,可以在文件开头编写以下指令:
<#@ template language="VB" #>
用于编写控制块代码的语言与生成的文本的语言无关。
标准控制块
标准控制块是生成输出文件部件的程序代码节。
在模板文件中,可以混合使用任意数量的文本块和标准控制块。 但是,不能在控制块中嵌套控制块。 每个标准控制块都以 <# ... #> 符号分隔。
例如,如果使用下面的控制块和文本块,则输出文件包含行“0, 1, 2, 3, 4 Hello!”:
<# for(int i = 0; i < 4; i++) { Write(i + ", "); } Write("4");
#> Hello!
您可以交错文本和代码,而不必使用显式 Write() 语句。 以下示例输出“Hello!”四次:
<# for(int i = 0; i < 4; i++) { #> Hello! <# } #>
在代码中,可以使用 Write(); 语句的位置都可以插入文本块。
![]() |
---|
在复合语句(例如循环或条件语句)中嵌入文本块时,请始终使用大括号 {...} 来包含文本块。 |
表达式控制块
表达式控制块计算表达式并将其转换为字符串。 该字符串将插入到输出文件中。
表达式控制块以 <#= ... #> 符号分隔。
例如,如果使用下面的控制块,则输出文件包含“5”:
<#= 2 + 3 #>
请注意,开始符号有三个字符“<#=”。
表达式可以包含作用域中的任何变量。 例如,下面的块输出数字行:
<#@ output extension=".txt" #> <# for(int i = 0; i < 4; i++) { #> This is hello number <#= i+1 #>: Hello! <# } #>
类功能控制块
类功能控制块定义属性、方法或不应包含在主转换中的所有其他代码。 类功能块常用于编写帮助器函数。 通常,类功能块位于单独的文件中,这样它们可以包含在多个文本模板中。
类功能控制块以 <#+ ... #> 符号分隔。
例如,下面的模板文件声明并使用一个方法:
<#@ output extension=".txt" #> Squares: <# for(int i = 0; i < 4; i++) { #> The square of <#= i #> is <#= Square(i+1) #>. <# } #> That is the end of the list. <#+ // Start of class feature block private int Square(int i) { return i*i; } #>
类功能必须编写在文件末尾。 不过,即使 include 指令后跟标准块和文本,也可以 <#@include#> 包含类功能的文件。
有关控制块的更多信息,请参见T4 文本模板控制块。
类功能块可以包含文本块
可以编写生成文本的方法。 例如:
List of Squares: <# for(int i = 0; i < 4; i++) { WriteSquareLine(i); } #> End of list. <#+ // Class feature block private void WriteSquareLine(int i) { #> The square of <#= i #> is <#= i*i #>. <# } #>
将文本生成方法放置在可供多个模板包含的单独文件中,是非常有用的。
程序集
模板的代码块可以使用定义了最常用 .NET 程序集(如 System.dll)的类型。 此外,也可以引用其他 .NET 程序集或您自己的程序集。 您可以提供程序集的路径或强名称:
<#@ assembly name="System.Xml" #>
应该使用绝对路径名,或在路径名中使用标准宏名。 例如:
<#@ assembly name="$(SolutionDir)library\MyAssembly.dll" #>
有关宏的列表,请参见用于生成命令和属性的宏。
在预处理文本模板中,assembly 指令无效。
有关更多信息,请参见 T4 程序集指令。
命名空间
import 指令与 C# 中的 using 子句或 Visual Basic 中的 imports 子句相同。 通过该指令,不使用完全限定名就可以在代码中引用类型:
<#@ import namespace="System.Xml" #>
可以根据需要使用任意多个 assembly 和 import 指令。 必须将它们放在文本块和控制块之前。
有关更多信息,请参见 T4 导入指令。
包含代码和文本
include 指令插入其他模板文件的文本。 例如,下面的指令插入 test.txt 的内容。
<#@ include file="c:\test.txt" #>
在处理时,被包含内容就像是包含文本模板的组成部分一样。 不过,即使 include 指令后跟普通文本块和标准控制块,也可以包含编写有类功能块 <#+...#> 的文件。
有关更多信息,请参见 T4 包含指令。
实用工具方法
对于文本模板而言,最有用的应用是根据源(如模型、数据库或数据文件)的内容生成材料。 模板提取数据并重新设置数据格式。 模板集合可以将此类源转换为多个文件。
有几种方法可以读取源文件:
在文本模板中读取文件。 下面是将数据读入模板的最简单的方法:
<#@ import namespace="System.IO" #> <# string fileContent = File.ReadAllText(@"C:\myData.txt"); ...
将文件作为可导航模型加载。 更有效的方法是将数据作为模型读取,文本模板代码可以导航该模型。 例如,可以加载 XML 文件,然后使用 XPath 表达式对其导航。 还可以使用 xsd.exe 创建一组类,使用这些类可读取 XML 数据。
在关系图或窗体中编辑模型文件。使用 Domain-Specific Language Tools 提供的工具可以将模型作为关系图或 Windows 窗体进行编辑。 这样便于与生成的应用程序的用户讨论模型。 Domain-Specific Language Tools 还创建一组反映模型结构的强类型类。 有关更多信息,请参见从域特定语言生成代码。
使用 UML 模型。 可以从 UML 模型生成代码。 这种方式的优点在于,可以以熟悉的表示法将模型作为关系图进行编辑。 此外,无需设计关系图。 有关更多信息,请参见如何:从 UML 模型生成文件。
相对文件路径
如果要引用相对于文本模板的位置中的文件,可以使用 this.Host.ResolvePath()。 还必须在 template 指令中设置 hostspecific="true":
<#@ template hostspecific="true" language="C#" #> <#@ output extension=".txt" #> <#@ import namespace="System.IO" #> <# // Find a path within the same project as the text template: string myFile = File.ReadAllText(this.Host.ResolvePath("MyFile.txt")); #> Content of MyFile.txt is: <#= myFile #>
还可以获取主机提供的其他服务。 有关更多信息,请参见从 T4 文本模板访问 Visual Studio 或其他主机。
文本模板在单独的 AppDomain 中运行
请注意,文本模板在与主应用程序分开的 AppDomain 中运行。 在大多数情况下这并不重要,但在某些复杂的情况下您可能会发现一些限制。 例如,如果要从单独的服务将数据传入模板或从中传出数据,则该服务必须提供可序列化的 API。