
14.3 T4的工作原理
从T4模板中生成文件的过程包含两个基本步骤。第一步,使用.tt文件生成一个标准的.NET类。这个类继承了abstract (MustInherit) Microsoft.VisualStudio.TextTemplating.TextTransformation类,并且重写了TransformText()方法。
第二步,创建并配置这个类的一个实例,调用TransformText方法。该方法返回一个字符串,用作所生成文件的内容。
一般情况下,看不到生成的类文件,但可以配置T4引擎,打开模板的调试功能,以制作类文件的副本。这只需要把template指令的debug特性设置为true,并保存模板文件。
在Debug模式下执行T4模板后,就会在系统的临时文件夹中创建许多文件。其中一个文件有任意名称,但扩展名是.cs或.vb (取决于模板语言)。该文件包含实际的生成器类。
打开Visual Studio命令提示符窗口,输入echo %TEMP%命令,就可以找到系统的临时文件夹。
这段代码包含许多支持模板调试的预处理器指令,但会使代码难以理解。下面的代码文件是从上一节的FinancialSample.tt模板中生成的,但重新进行了格式化,并删除了这些指令:
C#
namespace Microsoft.VisualStudio.TextTemplatingBE7601CBE8A6858147D586FD8FC4C6F9 { using System; public class GeneratedTextTransformation : Microsoft.VisualStudio.TextTemplating.TextTransformation { public override string TransformText() { try { this.Write("\r\nFinancial Sample Data\r\n"); for( int i = -5; i <= 5; i++ ) { WriteFinancialNumber(i); WriteLine( "" ); } this.Write("End of Sample Data\r\n\r\n "); } catch (System.Exception e) { System.CodeDom.Compiler.CompilerError error = new System.CodeDom.Compiler.CompilerError(); error.ErrorText = e.ToString(); error.FileName = "C:\\dev\\Chapter 14\\Chapter 14\\Finance.tt"; this.Errors.Add(error); } return this.GenerationEnvironment.ToString(); } void WriteFinancialNumber(decimal amount) { if( amount < 0 ) Write("({0:#0.00})", System.Math.Abs(amount) ); else Write("{0:#0.00}", amount); } } }
VB
Imports System Namespace Microsoft.VisualStudio.TextTemplating2739DD4202E83EF5273E1D1376F8FC4E Public Class GeneratedTextTransformation Inherits Microsoft.VisualStudio.TextTemplating.TextTransformation Public Overrides Function TransformText() As String Try Me.Write(""&Global.Microsoft.VisualBasic.ChrW(13) _ & Global.Microsoft.VisualBasic.ChrW(10) _ & "Financial Sample Data" _ & Global.Microsoft.VisualBasic.ChrW(13) _ & Global.Microsoft.VisualBasic.ChrW(10)) _ For i as Integer = -5 To 5 WriteFinancialNumber(i) WriteLine( "" ) Next Me.Write("End of Sample Data" _ & Global.Microsoft.VisualBasic.ChrW(13) _ & Global.Microsoft.VisualBasic.ChrW(10) _ & Global.Microsoft.VisualBasic.ChrW(13) _ & Global.Microsoft.VisualBasic.ChrW(10)&" ") Catch e As System.Exception Dim [error] As System.CodeDom.Compiler.CompilerError = _ New System.CodeDom.Compiler.CompilerError() [error].ErrorText = e.ToString [error].FileName = "C:\\dev\\Chapter 14\\Chapter 14\\Finance.tt" Me.Errors.Add([error]) End Try Return Me.GenerationEnvironment.ToString End Function Sub WriteFinancialNumber(amount as Decimal) If amount < 0 Then Write("(${0:#0.00})", System.Math.Abs(amount) ) Else Write("${0:#0.00}", amount) End If End Sub End Class End Namespace
注意这段代码中几个有趣的地方。首先,模板是通过运行TransformText()方法来执行的。这个方法的内容在try-catch块的内部执行,以捕获并存储所有的错误。Visual Studio 2015知道如何检索这些错误,并显示在正常的错误工具窗口中。
其次是使用了Write()方法。每个Text块都转换为一个字符串,并传送给Write()方法。实际上, Text块添加到GenerationEnvironment属性中,再转换为一个字符串,并返回给T4引擎。
Statement块和Class Feature块全部复制到生成的类中。区别是它们显示的位置。Statement块显示在TransformText()方法中,而Class Feature块显示在TransformText()方法之后,并且作用域相同。这说明可以在Class Feature块中声明哪些对象。
最后计算Expression块,把结果传送给Microsoft.VisualStudio.TextTemplating.ToStringHelper. ToStringWithCulture()方法。这个方法返回一个字符串,该字符串传送回Write()方法,就好像它是一个Text块。注意,在ToStringHelper从表达式中生成字符串时,要考虑特定的区域性。可以将这个区域性指定为template指令的一个特性。
TransformText()方法执行完毕后,就把一个字符串传送回主机环境,这里是Visual Studio 2015。主机环境决定如何处理该字符串。Visual Studio对这个任务使用output指令。指令是下一节将要介绍的主题。
在继续之前应该注意,上一段指出,T4不需要在Visual Studio内部运行。有一个命令行工具TextTransform.exe,位于%CommonProgramFiles%\microsoft shared\TextTemplating\14.0\文件夹下(在64位计算机上位于C:\Program Files(x86)\Common Files\microsoft shared\TextTemplating\14.0\)。尽管可以使用这个工具在构建过程中生成文件,但T4本身需要某些随Visual Studio一起安装的库,才能运行。这意味着,如果有一台单独的生成计算机,就需要安装Visual Studio。在Visual Studio中,扩展名为.tt的文件使用一个定制工具TextTemplatingFileGenerator处理。