Visual Studio 2015高级编程(第6版)
上QQ阅读APP看书,第一时间看更新

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处理。