如果在 Visual Studio 中生成程序代码或其他应用程序资源,这些一般准则可能很有用。 它们不是固定规则。
Design-Time T4 模板指南
设计时 T4 模板是在设计时在 Visual Studio 项目中生成代码的模板。 有关详细信息,请参阅 T4 文本模板设计时代码生成。
生成应用程序的不同变量特性。
代码生成对可能会在项目期间更改的应用程序的某些方面,或者会在不同版本中发生变化的部分最有用。 将这些变量方面与更固定的方面分开,以便更轻松地确定必须生成的内容。 例如,如果应用程序提供网站,请将提供函数的标准页面与定义从一个页面到另一个页面的导航路径的逻辑分开。
对一个或多个源模型中的变量方面进行编码。
模型是每个模板读取的文件或数据库,以获取要生成的代码的变量部分的特定值。 模型可以是数据库、XML 文件(你自行设计)、关系图或领域特定语言。 通常,一个模型用于在 Visual Studio 项目中生成多个文件。 每个文件都是从单独的模板生成的。
可以在项目中使用多个模型。 例如,可以定义用于网页之间导航的模型,以及页面布局的单独模型。
将模型集中在用户的需求和词汇上,而不是你的实现。
例如,在网站应用程序中,预期模型引用网页和超链接。
理想情况下,请选择适合模型所表示信息的呈现形式。 例如,网站导航路径的模型可以用框和箭头构成的图示来表示。
测试生成的代码。
使用手动或自动测试来验证生成的代码是否与用户要求一样工作。 避免从生成代码的同一模型生成测试。
在某些情况下,可以直接对模型执行常规测试。 例如,可以编写一个测试,确保可以通过从任何其他页面导航访问网站中的每个页面。
允许自定义代码:生成局部类。
除了生成的代码外,还允许手动编写的代码。 代码生成方案能够考虑可能出现的所有可能变体是不寻常的。 因此,你需要预期可能会添加或重写某些生成的代码。 如果生成的材料采用 .NET 语言(如 C# 或 Visual Basic),则两种策略特别有用:
生成的类应是部分类。 这样,便可以将内容添加到生成的代码。
类应成对生成,一个继承自另一个类。 基类应包含所有生成的方法和属性,派生类应仅包含构造函数。 这样,手写代码就可以覆盖任何自动生成的方法。
在其他生成的语言(如 XML)中,使用 <#@include#> 指令来简单组合手写和生成的内容。 在更复杂的情况下,可能需要编写一个后处理步骤,该步骤将生成的文件与任何手动写入的文件组合在一起。
将常用材料移动到包含文件或运行时模板中。
若要避免在多个模板中重复类似的文本和代码块,请使用该 <#@ include #> 指令。 有关详细信息,请参阅 T4 Include 指令。
还可以在单独的项目中生成运行时文本模板,然后从设计时模板调用它们。 为此,请使用 <#@ assembly #> 指令访问单独的项目。
请考虑将大块代码移到单独的程序集。
如果你有大型代码块和类功能块,那么将其中一些代码移动到在单独的项目中编译的方法可能很有用。 可以使用 <#@ assembly #> 该指令访问模板中的代码。 有关详细信息,请参阅 T4 程序集指令。
可以将方法放入模板可以继承的抽象类中。 抽象类必须继承自 Microsoft.VisualStudio.TextTemplating.TextTransformation. 有关详细信息,请参阅 T4 模板指令。
生成代码,而不是配置文件。
编写变量应用程序的一种方法是编写接受配置文件的泛型程序代码。 以这种方式编写的应用程序非常灵活,可以在业务需求发生变化时重新配置,而无需重新生成应用程序。 但是,此方法的一个缺点是,应用程序的性能将低于更具体的应用程序。 此外,其程序代码将更加难以读取和维护,部分原因是它始终处理大多数泛型类型。
相比之下,在编译前生成变量部件的应用程序可以进行强类型化。 这样,编写手写代码并将其与软件生成的部分集成可以更加简单、更可靠。
若要获得代码生成的全部优势,请尝试生成程序代码而不是配置文件。
使用生成的代码文件夹。
将模板和生成的文件放置在名为 “生成代码”的项目文件夹中,以明确说明这些不是应直接编辑的文件。 如果创建自定义代码来替代或添加到生成的类,请将这些类放置在名为 “自定义代码”的文件夹中。 典型项目的结构如下所示:
MyProject
Custom Code
Class1.cs
Class2.cs
Generated Code
Class1.tt
Class1.cs
Class2.tt
Class2.cs
AnotherClass.cs
预处理的 T4 运行时模板指南
将通用材料移动到继承的模板中。
可以使用继承在 T4 文本模板之间共享方法和文本块。 有关详细信息,请参阅 T4 模板指令。
你还可以使用包含运行时模板的文件。
将大型代码主体移动到分部类中。
每个运行时模板生成一个与模板同名的分部类定义。 可以编写包含同一类的另一部分定义的代码文件。 可以采用这种方式向类添加方法、字段和构造函数。 可以从模板中的代码块调用这些成员。
这样做的优点是代码更易于编写,因为 IntelliSense 可用。 此外,还可以在演示文稿和基础逻辑之间实现更好的分离。
例如,在 MyReportText.tt:
The total is: <#= ComputeTotal() #>
在 MyReportText-Methods.cs:
private string ComputeTotal() { ... }
允许自定义代码:提供扩展点。
请考虑在<#+ 类特性块>中生成虚拟方法。 这允许在很多上下文中使用单个模板,而无需修改。 可以构造提供最小附加逻辑的派生类,而不是修改模板。 派生类可以是常规代码,也可以是运行时模板。
例如,在 MyStandardRunTimeTemplate.tt 中:
This page is copyright <#= CompanyName() #>.
<#+ protected virtual string CompanyName() { return ""; } #>
在应用程序的代码中:
class FabrikamTemplate : MyStandardRunTimeTemplate
{
protected override string CompanyName() { return "Fabrikam"; }
}
...
string PageToDisplay = new FabrikamTemplate().TextTransform();
所有 T4 模板指南
将数据收集与文本生成分开。
尝试避免将计算过程与文本块混合。 在每个文本模板中,使用第一 <个 # 代码块 #> 设置变量和执行复杂计算。 从第一个文本块向下到模板末尾或第一个 <#+ 类特征块 #>,避免长表达式,避免循环和条件,除非它们包含文本块。 这种做法使模板更易于阅读和维护。
请勿将 .tt 用于包含文件。
请为包含文件使用不同的文件扩展名,例如 .ttinclude。
.tt仅用于要作为运行时或设计时文本模板处理的文件。 在某些情况下,Visual Studio 可识别 .tt 文件并自动设置其处理属性。
将每个模板作为固定原型启动。
编写要生成的代码或文本的示例,并确保它正确。 然后,将其扩展更改为 .tt,并通过读取模型以增量方式插入修改内容的代码。
请考虑使用类型化模型。
虽然可以为模型创建 XML 或数据库架构,但创建域特定语言(DSL)可能很有用。 DSL 的优点在于它生成一个类来表示架构中的每个节点,以及使用特性来表示属性。 这意味着你可以根据业务模型进行编程。 例如:
Team Members:
<# foreach (Person p in team.Members)
{ #>
<#= p.Name #>
<# } #>
请考虑为模型使用关系图。
许多模型最有效地以简单的文本表形式呈现和管理,尤其是在它们非常大时。
但是,对于某些类型的业务需求,必须阐明复杂的关系集和工作流,关系图是最适合的媒介。 关系图的优点是,可以轻松地与用户和其他利益干系人讨论。 通过在业务需求级别从模型生成代码,可以在需求更改时使代码更加灵活。
您还可以将自己的图表类型设计为领域特定语言(DSL)。 可以从 UML 和 DSL 生成代码。 有关详细信息,请参阅 分析和建模体系结构。