一、代码生成器系列-T4模板学习整理

一、T4简介

T4(Text Template Transformation Toolkit)在 Visual Studio 中,“T4 文本模板”是由一些文本块和控制逻辑组成的混合模板,它可以生成文本文件。 在 Visual C# 或 Visual Basic 中,控制逻辑编写为程序代码的片段。生成的文件可以是任何类型的文本,例如网页、资源文件或任何语言的程序源代码。

T4 文本模板有两种类型:
  1、运行时模板  可在应用程序中执行运行时 T4 文本模板(“预处理过的”模板)以便生成文本字符串(通常作为其输出的一部分)。
  若要创建运行时模板,请向您的项目中添加“已预处理的文本模板”文件。
  另外,您还可以添加纯文本文件并将其“自定义工具”属性设置为“TextTemplatingFilePreprocessor”。

  2、设计时模板
  在 Visual Studio 中执行设计时 T4 文本模板,以便定义应用程序的部分源代码和其他资源。
  通常,您可以使用读取单个输入文件或数据库中的数据的多个模板,并生成一些 .cs、.vb 或其他源文件。
  每个模板都生成一个文件。 在 Visual Studio 或 MSBuild 内执行它们。
  若要创建设计时模板,请向您的项目中添加“文本模板”文件。 另外,您还可以添加纯文本文件并将其“自定义工具”属性设置为“TextTemplatingFileGenerator”。

二、编写第一个T4模板例子-HelloWorld

通过最简单的HelloWorld例子,来了解一下T4的基本结构
1、在项目中【添加新项】:

一、代码生成器系列-T4模板学习整理_第1张图片

2、HelloWorld.tt模板文件代码:

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>

using System;

namespace HelloWorld
{
    public class HelloWorldMain
    {
        public static void Main(string[] args)
        {
            <# 
            List listUser = GetUserList();
            foreach(SysUser user in listUser)
            {
            #>
            Console.WriteLine("你好 {0}({1}),欢迎使用 T4 !", "<#= user.UserName #>", "<#= user.UserCode #>");
            <#}
            #>
        }
    }
}


<#+
//类
public class SysUser
{
    public string UserCode { get; set; }
    public string UserName { get; set; }
    public SysUser(string usercode,string username)
    {
        this.UserCode = usercode;
        this.UserName = username;
    }
}
//初始化众人
public List GetUserList()
{
    List list = new List();
    list.Add(new SysUser("admin","超级管理员"));
    list.Add(new SysUser("zhangsan","张三"));
    list.Add(new SysUser("km","键盘男"));
    return list;
}

#>

保存模板文件后会自动生成文件HelloWorld.cs,代码:


using System;

namespace HelloWorld
{
    public class HelloWorldMain
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("你好 {0}({1}),欢迎使用 T4 !", "超级管理员", "admin");
            Console.WriteLine("你好 {0}({1}),欢迎使用 T4 !", "张三", "zhangsan");
            Console.WriteLine("你好 {0}({1}),欢迎使用 T4 !", "键盘男", "km");
        }
    }
}

从生成的代码可以看出,模板的核心内容可以描述成是:通过程序脚本逻辑生成文本的过程。

二、T4模板基本结构

T4模板由一下5个“块”部件组成:

1). 指令块(Directive Block)
2). 文本块(Text Block)
3). 码语句块(Statement Block)
4). 表达式块(Expression Block)
5). 类特性块(Class Feature Block)

1、指令块(Directive Block)

和ASP.NET页面的指令一样,它们出现在文件头,通过<#@…#>表示。其中<#@ template …#>指令是必须的,用于定义模板的基本属性,比如编程语言、基于的文化、是否支持调式等等。比较常用的指令还包括用于程序集引用的<#@ assembly…#>,用于导入命名空间的<#@ import…#>等等。

指令通常是模板文件或包含的文件中的第一个元素。不应将它们放置在代码块 <#…#> 内,也不应放置在类功能块 <#+…#> 之后。

T4 模板指令

<#@ template [language="C#"] [hostspecific="true"] [debug="true"] [inherits="templateBaseClass"] [culture="code"] [compilerOptions="options"] #>

1.模版指令中所有特性均为可选的。
2.langeuage:输出语言,有效值C#、VB,默认为C#。
3.debug:是否启用调试,有效值true、false,默认为false。特别说明下这个调试真的不咋地,很容易让VS崩溃,很鸡肋的功能。
4.hostspecific:有效值true、false,默认为false。如果将此特性的值设置为 true,则会将名为 Host 的属性添加到由文本模板生成的类中。 该属性是对转换引擎的宿主的引用,并声明为Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost。
5.inherits:可以指定模板的程序代码可以继承自另一个类,这个类也可以从文本模板生成。目前木有使用过,基本上可以忽略。
6.compilerOptions:有效值为任何有效的编译器选项。基本上可以忽略 。

T4 参数指令
<#@ parameter type="Full.TypeName"name="ParameterName"#>
- type:有效值是传递参数的类型名。
- name:传递参数的名。

T4 输出指令
<#@ output extension=".fileNameExtension"[encoding="encoding"] #>
比较重要的指令,用于设置输出文件的后缀名和文件编码。
- extension:输出文件扩展名,默认为”.cs”。
- encoding:文件编码。

T4 程序集指令
<#@ assembly name="[assembly strong name|assembly file name]"#>
1.程序集指令相当于VS里面我们添加程序集引用的功能,该指令只有一个参数name,用以指定程序集名称,如果程序集已经在GAC里面注册,那么只需要写上程序集名称即可,如<#@ assembly name=”System.Data.dll” #>,否则需要指定程序集的物理路径。
2.T4模版的程序集引用是完全独立的,也就是说我们在项目中引用了一些程序集,然后项目中添加了一个T4模版,T4模版所需要的所有程序集引用必须明确的在模版中使用程序集执行引用才可以。
3.T4模版自动加载以下程序集Microsoft.VisualStudio.TextTemplating.1*.dll、System.dll、WindowsBase.dll,如果用到了其它的程序集需要显示的使用程序集添加引用才可以。
4.可以使用 (variableName)语法引用VisualStudio或MSBuild变量(如 (SolutionDir)),以及使用 %VariableName% 来引用环境变量。介绍几个常用的$(variableName) 变量:

         - $(SolutionDir):当前项目所在解决方案目录
         - $(ProjectDir):当前项目所在目录
    - $(TargetPath):当前项目编译输出文件绝对路径
   - $(TargetDir):当前项目编译输出目录,即web项目的Bin目录,控制台、类库项目bin目录下的debug或release目录(取决于当前的编译模式)
          - $(SolutionDir):当前项目所在解决方案目录
   - $(ProjectDir):当前项目所在目录
   - $(TargetPath):当前项目编译输出文件绝对路径
   - $(TargetDir):当前项目编译输出目录,即web项目的Bin目录,控制台、类库项目bin目录下的debug或release目录(取决于当前的编译模式)
    举个例子:比如我们在D盘根目录建立了一个控制台项目TestConsole,解决方案目录为D:\LzrabbitRabbit,项目目录为
    D:\LzrabbitRabbit\TestConsole,那么此时在Debug编译模式下
   - $(SolutionDir)的值为D:\LzrabbitRabbit
   - $(ProjectDir)的值为D:\LzrabbitRabbit\TestConsole
   - $(TargetPath)值为D:\LzrabbitRabbit\TestConsole\bin\Debug\TestConsole.exe
   - $(TargetDir)值为D:\LzrabbitRabbit\TestConsole\bin\Debug\

    举个例子:比如我们在D盘根目录建立了一个控制台项目TestConsole,解决方案目录为D:\LzrabbitRabbit,项目目录为
    D:\LzrabbitRabbit\TestConsole,那么此时在Debug编译模式下
    - $(SolutionDir)的值为D:\LzrabbitRabbit
    - $(ProjectDir)的值为D:\LzrabbitRabbit\TestConsole
    - $(TargetPath)值为D:\LzrabbitRabbit\TestConsole\bin\Debug\TestConsole.exe
    - $(TargetDir)值为D:\LzrabbitRabbit\TestConsole\bin\Debug\

T4 导入指令
<#@ import namespace="namespace"#>
在 Visual Studio T4 文本模板的代码块中,import 指令允许您在不提供完全限定名称的情况下引用另一个命名空间中的元素。 它等效于 C# 中的 using 或 Visual Basic 中的 imports。默认已经导入了System命名空间的引用。

T4 包含指令
<#@ include file="filePath"#>
1.filePath 可以是绝对的,或相对于当前模板文件。
2.filePath 可以包括用“%”分隔的环境变量。 例如:<#@ include file=”%HOMEPATH%\MyIncludeFile.t4” #>
3.所包含的文件的名称不必使用扩展名“.tt”。可能需要针对包含的文件使用其他扩展名,例如,“.t4”。 这是因为,在您将 .tt 文件添加到项目中时,Visual Studio 会自动将其“自定义工具”属性设置为 TextTemplatingFileGenerator。 您通常不希望单独转换包含的文件。
4.在处理时,被包含内容就像是包含文本模板的组成部分一样。 不过,即使 include 指令后为普通文本块和标准控制块,也可以包括含有类功能块 <#+…#> 的文件。
5.包含指令可以提高代码复用率,比如我们可以将一些常用的程序集、命名空间引用放到一个文件里,使用时仅需要引用下即可,省去了每次都要重新引用一遍的烦恼,如我们建立Reference.ttinclude文件,里面包含了我们平时常用的程序集引用。

 
  
 
  
<#
    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;
}
#>

5、类特性块(Class Feature Block)

如果文本转化需要一些比较复杂的逻辑,我们需要写在一个单独的辅助方法中,甚至是定义一些单独的类,我们就是将它们定义在类特性块中。类特性块的表现形式为<#+ FeatureCode #>,对于Hello World模板,得到人名列表的GetUserList方法就定义在类特性块中。(上面类功能控制块中有描述)

T4文本模板编写是T4模板运用的基础,掌握了这些简单的编写规则后,我们就可以编写基于T4模板的代码生成器了

你可能感兴趣的:(C#.NET)