[译]TaHoGen简介– CodeSmith样式代码生成引擎的开源实现
By robot-v1.0
本文链接 https://www.kyfws.com/applications/introducing-tahogen-an-open-source-implementation-zh/
版权声明 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
- 26 分钟阅读 - 12648 个词 阅读量 0[译]TaHoGen简介– CodeSmith样式代码生成引擎的开源实现
原文地址:https://www.codeproject.com/Articles/9844/Introducing-TaHoGen-An-Open-Source-Implementation
原文作者:Philip Laureano
译文由本站 robot-v1.0 翻译
前言
Looking for multiple file output support from a single template in one pass? Then look no further.
一口气从单个模板中寻找多种文件输出支持?然后,再没有其他.
- 下载TaHoGen引擎源-157 Kb(Download the TaHoGen Engine source - 157 Kb)
- 下载TaHoGen引擎二进制文件-70.1 Kb(Download the TaHoGen Engine binaries - 70.1 Kb)
- 下载VB.NET和C#示例-65.9 Kb(Download the VB.NET and C# examples - 65.9 Kb)
介绍(Introduction)
那么,TaHoGen是什么,您可能会问?(So, what is TaHoGen, you may ask?)
TaHoGen是100%(TaHoGen is a 100%)自由(free),具有以下功能的开源代码生成引擎(根据GPL授权):(, Open Source code generation engine (licensed under the GPL) with the following features:)
-
CodeSmith模板兼容性(CodeSmith Template Compatibility):解析器本身可以解析几乎所有CodeSmith模板.由于法律原因,它唯一无法解析的是Eric Smith的对象模型所特有的任何内容(例如SchemaExplorer等).(: The parser itself can parse almost any CodeSmith template. The only thing it cannot parse (for legal reasons) is anything specific to Eric Smith’s object model (such as SchemaExplorer and the like).)
-
多国语言支持(Multiple Language Support).与CodeSmith一样,TaHoGen将与任何CodeDom语言一起使用,例如C#,VB.NET,J#和JScript.(. Like CodeSmith, TaHoGen will work with any CodeDom language such as C#, VB.NET, J#, and JScript.)
-
多种语言的CodeBehind(CodeBehind in Multiple Languages).您可以在VB.NET中编写模板,并在包含以其他语言(如C#)编写的代码隐藏文件中.模板编译器将自动编译C#文件,并将其包含在已编译的VB.NET模板的程序集中.(. You can write a template in VB.NET, and include codebehind files written in other languages such as C#. The template compiler will automatically compile the C# file, and include it in the assembly of the compiled VB.NET template.)
-
本机子模板支持(Native SubTemplate Support).这使您可以执行诸如在模板内编译模板,运行模板,然后将新编译的模板传递给另一个模板等操作,以便其再次使用.您甚至可以使用模板来生成另一个模板,该模板又可以生成另一个模板.无论如何,你明白了.(. This allows you to do things such as compile templates within templates, run the template, and then pass that newly compiled template to another template so that it can reuse it again. You can even use a template to generate another template which, in turn, can generate another template. Anyway, you get the idea.)
-
单个模板,多个输出(Single Template, Multiple Outputs).您可以将单个模板的输出同时发送到以下一个或多个目标:(. You can send the output of a single template to one or more of the following targets at the same time:)
- 控制台输出(StdOut)(Console Output (StdOut))
- 调试窗口(Debug Window)
- 跟踪窗口(Trace Window)
- 剪贴板(实验性)(Clipboard (experimental))
- 文件输出(File Output)
- 流输出(Stream Output)
- 字符串输出(将结果发送到目标字符串)(String Output (send the results to a target string))
-
多个模板,一个已编译的程序集(Multiple Templates, One Compiled Assembly). TaHoGen允许您获取多个模板文件并将其编译为单个程序集.如果愿意,您甚至可以命名模板并将它们分成同一程序集中的不同名称空间.(. TaHoGen allows you to take multiple template files and compile them into a single assembly. You can even name your templates and separate them into different namespaces within the same assembly, if you wish.)
-
复合模板(Composite Templates).任何模板都可以在运行时与另一个模板"链接"在一起,以形成更复杂的模板.例如,您可以将类生成器模板与SQL模板结合使用,以一次性生成业务类和数据库.(. Any template can be “chained” together with another template at runtime to form even more complex templates. For example, you can combine a class generator template with an SQL template to generate both your business classes and database in one pass.)
-
共享属性集(Shared Property Sets).这使您可以一次在单个对象上设置一组属性,然后将该属性对象传递给所有模板,以便它们都可以从该属性集中读取.您甚至可以使用此属性集将子模板传递给其他模板!(. This allows you to set a group of properties on a single object once, and then pass that property object around to all of your templates so that they can all read from that property set. You can even use this property set to pass subtemplates to other templates!)
-
非常非常快(Very, Very Fast).解析器后端是使用C ++表达式模板编写的,因此解析时间非常快.此外,(. The parser backend is written using C++ expression templates, making parsing time extremely fast. In addition,)解析器和CodeDom编译器均被缓存(both the the parser and the CodeDom compilers are cached),这意味着没有一个以上的唯一模板会被解析和编译一次以上,从而使模板的构建时间更快.(, meaning that no single, unique template will be parsed and compiled more than once—making the template build time even faster.) 与CodeSmith一样,TaHoGen是代码生成器生成器.它解析模板文件中的文本,并将该文本转换为CodeDom图,然后将其编译(使用您选择的.NET语言)为自定义文本生成器,该生成器将精确输出您指定的文本.(Like CodeSmith, TaHoGen is a code generator generator. It parses the text from template files and converts that text into a CodeDom graph which, in turn, is compiled (using the .NET language of your choice) into a customized text generator which outputs exactly the text that you specify.)
TaHoGen不是什么(What TaHoGen is not)
与商业版本不同,TaHoGen并未配备精美的GUI.实际上,以其最原始的形式,它甚至根本没有GUI.在第一篇文章中,我将向您展示如何使用引擎本身.在本系列的下一篇文章中,我们将创建一个用于运行模板的简单GUI,甚至将其集成到VS.NET IDE中,以便我们可以将模板的输出直接发送到代码窗口,但是那是以后的事.现在,请坐着,我保证我会为您提供这篇当前文章的宝贵时间.(Unlike its commercial counterpart, TaHoGen doesn’t come with a fancy GUI out of the box. In fact, in its rawest form, it doesn’t even have a GUI at all. In this first article, I will show you how to use the engine itself. In the next article in this series, we’ll create a simple GUI for running our templates, and we’ll even integrate it into the VS.NET IDE so that we can send the output of the template directly to the code window—but that’s for later. For now, sit tight, and I promise that I will make this current article worth your while.)
背景(Background)
最初,我着迷于Eric Smith(<(At first, I was fascinated with how Eric Smith (author of) 史密斯(CodeSmith) )能够解析ASP样式的文本文件并将其转换为模板.我想自己学习而不需要学习正则表达式,因此我研究了一些替代方法,例如ANTLR,Bison/Lex和GoldParser.对我而言,ANTLR生成的代码简直是一场噩梦,而Bison/Lex对于我仅是凡人的接触而言,都是有点不朽的神秘.另一方面,GoldParser在解析上下文相关语法(例如ASP.NET)时遇到问题,因此也不可能.我需要使我能够逐步构建ASP标记解析器的东西,同时又能为我提供C ++生成的本机代码的速度.那边() was able to parse ASP-style text files and convert them into templates. I wanted to learn how to do this myself without having to learn regular expressions, so I looked at a few alternatives such as ANTLR, Bison/Lex, and GoldParser. For me, ANTLR-generated code was a nightmare to behold, and Bison/Lex was a little too immortally cryptic for my mere mortal reach. GoldParser, on the other hand, had problems parsing context-sensitive grammars such as ASP.NET, so that was out of the question as well. I needed something that would allow me to build an ASP tag parser incrementally, while at the same time affording me the speed of C++ generated native code. That’s where) 精神分析器(The Spirit Parser) 派上用场了.经过近三个月的修改,我终于有了正确的BNF语法,可以轻松解析大多数CodeSmith模板文件.语法完成后,就像其他很多好奇的编码器一样,他花了很多时间在手,我想:“如果我能为此编写一个解析器,为什么不花所有的精力从头开始编写自己的实现呢?"(came in handy. After tinkering with it for nearly three months, I finally got the right BNF grammar to parse most CodeSmith template files without a hitch. Once the grammar was done, like any other curious coder with a lot of time on his hands, I figured, “If I can write a parser for this, why not go all the way and write my own implementation from scratch?”)
..所以我做到了.现在,经过一年和近四次重写,终于可以为公众使用了! :)(..And so I did. Now, after a year and nearly four rewrites, it is finally ready for public consumption! :))
“我还是不明白–如果CodeSmith是免费的,您会写另一个引擎吗?"(“I still don’t get it – would you write another engine if CodeSmith is free anyway?”)
我花这么多时间编写另一个引擎的主要原因是,我觉得CodeSmith虽然非常有用,但却不能满足我的所有需求.像许多用户一样,我想一次将一个模板的输出发送到多个位置,并且我还想从一个模板生成多个文件,而又不求助于模板源中的一些小技巧(无罪,Eric).其次,我想在自己的个人项目中使用该引擎,但是我当然没有钱来支付许可证费用.第三,即使我有钱,也不会花钱购买没有我想要的所有功能的产品.最后,我想写一些对广大开发者社区有用的东西,以表示"谢谢"他们过去给予我的所有帮助,尤其是在CodeProject上.对我来说,这是一种爱的劳动,我希望您能像编码时一样开心地享受它!(The main reason why I spent this much time writing another engine is that I felt that CodeSmith, while extremely useful, didn’t fit all of my needs. Like many users, I wanted to send the output of a single template to multiple locations at once, and I also wanted to generate multiple files from a single template without resorting to a few hacks in the template source (no offense, Eric). Secondly, I wanted to use the engine in my own personal projects, but I certainly didn’t have the funds to pay for a license. Third, even if I had the money, I wasn’t going to pay for a product that didn’t have all the features that I was looking for. Lastly, I wanted to write something that would be useful for the developer community at large as a way of saying “Thank you” for all of the help they’ve given me in the past, especially at CodeProject. For me, this was a labor of love, and I hope you enjoy it as much as I had fun coding it!)
使用代码(Using the code)
由于这只是一篇介绍性文章(也是我的第一篇文章!),因此我将向您展示刚好足以使您开始使用此库.希望(一旦我有更多时间),我们可以在本系列的后续文章中进一步研究该库的内部结构.就目前而言,以下各节已足够.本文将分为两个部分:第一,我将向您展示模板代码的外观,第二,将向您展示如何在自己的应用程序中使用引擎.(Since this is only an introductory article (and my very first article ever!), I’ll show you just enough to get you started in using this library. Hopefully (once I get more time), we can delve further into the internals of this library in future articles of this series. For now, the following sections will have to suffice. This article will be divided into two parts—first, I’ll show you what the template code looks like, and second, I will show you how you can use the engine in your own applications.)
最低建筑要求(Minimum Requirements for Building)
您将需要:(You are going to need:)
- Boost库和Spirit分析器框架(The Boost Libraries and the Spirit Parser Framework).你可以在(. You can get them at) 这个连结(this link) .(.)
- Visual Studio 2003(Visual Studio 2003).该项目大量使用了表达式模板作为解析器代码,因此您将需要VC ++ 7.1或更高版本来处理Spirit Library使用的模板.此外,您还需要C#编译器来构建用.NET编写的库的一部分.(. This project makes heavy use of expression templates for the parser code, so you’re going to need VC++ 7.1 or higher to handle the templates used by the Spirit Library. In addition, you’ll also need a C# compiler to build the portion of the library that is written in .NET.)
- Windows 2000/XP(Windows 2000/XP). TaHoGen使用COM Interop来弥合本机C ++代码和.NET之间的鸿沟,因此,目前,此实现仅在标准Microsoft版本的.NET Framework上运行. (我尚未在Mono上进行过测试,但是如果您可以在该平台上使用它,请告诉我!)(. TaHoGen uses COM Interop to bridge the gap between native C++ code and .NET, so for now, this implementation will only run on the standard Microsoft version of the .NET Framework. (I haven’t tested it on Mono just yet, but if you get it to work on that platform, let me know!))
- 非常耐心(A lot of patience).解析器使用了Spirit库中的一些非常复杂的C ++模板,这大大降低了构建时间.准备等待.(. The parser uses some pretty complicated C++ templates from the Spirit library, which significantly slows down build time. Be prepared to wait.)
开始之前您需要了解的内容(What You Need to Know Before We Begin)
本文假设您有一定的写作经验(This article assumes that you’ve had some experience with writing) 史密斯(CodeSmith) 模板,并且您熟悉VB.NET或C#中的编码.除此之外,让我们开始吧.(templates, and that you are familiar with coding in either VB.NET, or C#. With that aside, let’s get started.)
模板语言与CodeSmith的差异(Template Language Differences from CodeSmith)
TaHoGen和CodeSmith的模板语言之间的差异很小.在大多数情况下,它们是相同的,但有一些值得注意的例外.让我们从一个简单的示例开始-C#的属性获取/设置生成器:(The differences between the template languages of TaHoGen and CodeSmith are minimal. For the most part, they are identical, with a few notable exceptions. Let’s start with a simple example – a property get/set generator for C#:)
<%@ CodeTemplate ClassName="PropertyGenerator"
Namespace="MyTemplateNamespace" Language="C#" TargetLanguage="C#"%>
<%@ Property Name="Name" Type="System.String" Category="Options" %>
<%@ Property Name="Type" Type="System.String" Category="Options" %>
<%@ Property Name="ReadOnly" Type="System.Boolean"
Default="true" Category="Options" %>
public <%=Type%> <%=Name%>
{
get { return _<%=Name.Substring(0, 1).ToLower() +
Name.Substring(1)%>; }<%if (!ReadOnly) {%>
set { _<%=Name.Substring(0, 1).ToLower() +
Name.Substring(1)%> = value; }<%}%>
}
对于那些熟悉CodeSmith模板语法(或ASP.NET)的人来说,这是不言而喻的.上面的模板读取两个字符串,(For those of us who are familiar with CodeSmith’s template syntax (or ASP.NET), this is self explanatory. The template above reads two strings,) Name
和(and) Type
,并生成类似于以下内容的文本:(, respectively, and generates text similar to the following:)
public string MyProperty
{
get { return _myProperty; }
set { _myProperty = value; }
}
两种语法之间的区别在以下行中找到:(The difference between the two syntaxes is found in the following line:)
<%@ CodeTemplate ClassName="PropertyGenerator"
Namespace="MyTemplateNamespace" Language="C#" TargetLanguage="C#"%>
的(The) ClassName
属性将生成的模板的名称设置为”(attribute sets the name of the generated template to “) PropertyGenerator
“,而(”, while the) Namespace
属性将该模板分配给名为”(attribute assigns that template to a namespace named “) MyTemplateNamespace
“. TaHoGen然后将生成一个如下所示的模板类:(”. TaHoGen will then generate a template class that looks like this:)
namespace MyTemplateNamespace
{
using System;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing.Design;
using System.IO;
using TaHoGen.Generators;
using TaHoGen.Targets;
public class PropertyGenerator : TaHoGen.Generators.TextGenerator
{
private string _name;
private string _type;
private bool _readOnly;
public PropertyGenerator()
{
}
public PropertyGenerator(TaHoGen.PropertyBag propertyBag) :
this()
{
this.LoadProperties(propertyBag);
}
[Category("Options")]
public string Name
{
get
{
return this._name;
}
set
{
this._name = value;
}
}
[Category("Options")]
public string Type
{
get
{
return this._type;
}
set
{
this._type = value;
}
}
[Category("Options")]
public bool ReadOnly
{
get
{
return this._readOnly;
}
set
{
this._readOnly = value;
}
}
protected override void GenerateImpl(System.IO.TextWriter writer)
{
writer.Write("\r\npublic ");
writer.Write(Type);
writer.Write(" ");
writer.Write(Name);
writer.Write("\r\n\t\t{\r\n\t\t\tget { return _");
writer.Write(Name.Substring(0, 1).ToLower() + Name.Substring(1));
writer.Write("; }");
if (!ReadOnly) {
writer.Write("\r\n\t\t\tset { _");
writer.Write(Name.Substring(0, 1).ToLower() + Name.Substring(1));
writer.Write(" = value; }");
}
writer.Write("\r\n\t\t}\r\n\t\t");
}
}
}
从上面的示例中可以看到,设置模板的名称并将其分配给特定的名称空间是很简单的.如果您决定不为模板定义类名和名称空间,则无需担心-如果您忽略这些属性之一,那么将为您输入默认值.(As you can see from the example above, setting the name of the template and assigning it to a particular namespace is trivial. No need to worry if you decide not to define a class name and a namespace for your template—if you omit either one of these attributes, then the default values will be entered for you.)
其他语言的CodeBehind(CodeBehind in Other Languages)
TaHoGen中包含代码隐藏文件的语法与CodeSmith中的语法相同.唯一的区别是TaHoGen支持使用模板中当前使用的语言之外的其他语言来构建代码隐藏文件,如本示例所示:(The syntax for including a codebehind file in TaHoGen is the same as the one in CodeSmith. The only difference here is that TaHoGen supports building codebehind files in languages other than the one currently being used in the template, as shown in this example:)
<%@ CodeTemplate ClassName="MyTemplate"
Namespace="MyNamespace" Language="C#" TargetLanguage="C#"%>
<%@ Assembly Src="myclass1.vb" %> <%---Build a VB.NET source file--%>
<%@ Assembly Src="myclass2.js" %> <%---Build a JScript source file--%>
<%---The rest of your template would go here--%>
子模板支持(SubTemplate Support)
在构建时在模板中编译模板(Compiling Templates within Templates at Build Time)
该库使您可以在模板内编译模板,并自动将那些外部编译的模板作为模板主体的一部分包含在内.指令:(This library allows you to compile templates within templates and have those externally compiled templates automatically included as part of the main assembly of your template. The directives:)
<%@ Compile Template="Property.tgt" outputfilename="MyExternalAssembly.dll" %>
<%@ Import Namespace="MyTemplateNamespace"%>
…将编译上面的示例,并包括”(…will compile the example above and include the “) MyTemplateNamespace
“名称空间作为构建输出的一部分,并包含该已编译程序集的名称空间("(” namespace as part of the build output, and include the namespace of that compiled assembly (“)MyExternalAssembly.dll(MyExternalAssembly.dll)")作为主模板的一部分.(”) as part of your main template.)
在运行时编译模板(模板内)(Compiling Templates (within Templates) at Run Time)
您还可以在运行时从应用程序内部(甚至从自己的模板内部)构建模板.这是VB.NET中的完整示例:(You can also build templates at runtime from within your application (and even from within your own templates). Here is a complete example in VB.NET:)
Imports System
Imports System.Diagnostics
Imports System.IO
Imports System.Reflection
Imports TaHoGen
Imports TaHoGen.Targets
Module Module1
Sub Main()
Dim text As String
'Read the contents of the template
Dim reader As New StreamReader("property.tgt")
text = reader.ReadToEnd()
'Compile it into a single assembly
Dim templateAssembly As [Assembly] = TemplateCompiler.Compile(text)
'Did it succeed?
If templateAssembly Is Nothing Then
Console.WriteLine("Template Compilation Failed!")
Return
End If
'There's only going to be one template in this assembly
'so it's safe to return just the first one
Dim templateType As Type = templateAssembly.GetTypes()(0)
'This *should* work
Debug.Assert(Not (templateType Is Nothing))
'Set the properties for the template
Dim properties As New PropertyTable
properties("Type") = "string"
properties("Name") = "MyProperty"
properties("ReadOnly") = False
'Instantiate the template and assign the properties at the same time
Dim args As Object() = {properties}
Dim generator As ITextGenerator = _
CType(Activator.CreateInstance(templateType, args), ITextGenerator)
'We should have a valid generator at this point
Debug.Assert(Not (generator Is Nothing))
'Write to the console
Dim output As New ConsoleTarget
'Attach the output of the generator to the console
output.Attach(generator)
'Generate the output itself
output.Write()
End Sub
End Module
上面的大多数代码非常简单.致电(Most of the code above is pretty straightforward. The call to) TemplateCompiler.Compile()
将模板构建到一个程序集中,一旦编译该程序集,剩下要做的就是创建该模板的实例并运行它.请注意,尽管模板本身已实例化,但是代码从不直接调用模板来生成文本:(builds the template into an assembly, and once that assembly is compiled, the only thing left to do is to create an instance of that template and run it. Notice that although the template itself was instantiated, the code never calls the template directly to generate the text:)
Dim generator As ITextGenerator = _
CType(Activator.CreateInstance(templateType), ITextGenerator)
'Write to the console
Dim output As New ConsoleTarget
'Attach the output of the generator to the console
output.Attach(generator)
'Generate the output itself
output.Write()
相反,模板的输出将附加到控制台,并且模板将在执行后立即执行(Instead, the output of the template is attached to the console, and the template executes as soon as) Output.Write()
被调用.模板本身永远不会直接知道它将写入哪个目标.简而言之,输出目标和模板在它们各自的接口之外永远不会直接相互引用.这种方法使将许多输出附加到同一模板变得非常容易,反之亦然,对我而言,它在许多场合都派上了用场.(is invoked. The template itself never directly knows about which target it will be writing to. In short, the output targets and the templates never directly refer to each other outside of their respective interfaces. This approach makes it very easy to attach many outputs to the same template, and vice-versa, and for me, it has come in handy on many occasions.)
子模板作为模板属性(SubTemplates as Template Properties)
有时您可能希望保留模板的一部分处于打开状态,或者在该模板中定义一个区域,您可以在其中插入另一个模板的输出.这是您可以执行的一种方法:(There might be times where you would want to leave a portion of your template open, or define a region in that template where you could insert the output of another template. Here’s one way you can do it:)
<%@ CodeTemplate ClassName="SubTemplateSample" Language="C#" TargetLanguage="C#"%>
<%@ Property Name="FirstRegion" Type="ITextGenerator" Category="SubTemplates" %>
<%@ Property Name="SecondRegion" Type="ITextGenerator" Category="SubTemplates" %>
#region This is the First Region
<%=RunTemplate(FirstRegion)%>
#endregion
#region This is the Second Region
<%=RunTemplate(SecondRegion)%>
#endregion
现在,您可能想知道如何将模板分配给另一个模板的另一个属性.我们将在下一部分中处理.现在,您可以认为模板属性与基于原始类型的其他属性没有什么不同.(Now, you might be wondering how to assign a template to another property of another template. We’ll handle that in the next section. For now, you can think of template properties as being no different from other properties based on primitive types.)
从模板中执行属性子模板(Executing Property SubTemplates from within Your Template)
请注意,(Notice that both the) FirstRegion
和(and) SecondRegion
上面示例中的子模板是该类型的模板属性(subtemplates from the examples above are template properties of the type) ITextGenerator
. (这是所有模板都必须实现才能被识别为模板的基本接口).在这种情况下,我们将使用(. (This is the base interface that all templates must implement in order to be recognized as templates). In this case, we’re going to use the) ITextGenerator
用作存根的属性.每次致电(properties to act as stubs. Each call to the) RunTemplate()
方法检查当前属性是否附加有模板,并且是否附加有模板,它将运行该模板并将其输出插入模板的特定部分.否则,该部分将保持空白,就好像模板不存在一样.(method checks if there is a template attached to the current property, and if there is a template attached, it will run that template and insert its output into that particular section of the template. Otherwise, that section will remain blank as if the template never existed.)
引擎使用(Engine Usage)
现在,我们已经完成了模板语法的一些基础知识,我将向您展示如何将引擎集成到您自己的应用程序中.构建完整个解决方案后,您需要引用(Now that we’re done with some of the basics of the template syntax, I’m going to show you how to integrate the engine into your own application. Once you’ve built the entire solution, you’re going to need to reference the)**TaHoGen.Core.dll(TaHoGen.Core.dll)**在您的项目中组装以使用引擎.(assembly in your project in order to use the engine.)
开始之前的一些注意事项(A Few Notes before We Begin)
如果要构建或重建引擎的二进制文件,请确保拥有最新版本的(If you are going to build or rebuild the binaries for the engine, make sure you have the latest version of the) Boost库(Boost libraries) . (在撰写本文时,TaHoGen使用Boost v1.31).您将需要它来编译模板解析器.由于模板解析器是用C ++/COM编写的,因此您可能还需要运行(. (At the time of this writing, TaHoGen uses Boost v1.31). You’re going to need it to compile the template parser. Since the template parser is written in C++/COM, you also might need to run)**regsvr32.exe(regsvr32.exe)**上(on)**CodeSmithParser.dll(CodeSmithParser.dll)**在COM Interop生成后将其注册.无论如何,让我们继续进行讨论.(to register it with COM Interop once it has been built. Anyway, let’s go on to the discussion.)
TemplateCompiler类(The TemplateCompiler Class)
这是完成大部分工作的课程.它只有一个静态方法,(This is the class that does most of the work. It has a single static method,) Compile()
,它具有以下重载:(, which has the following overloads:)
// Methods for compiling a single template
public static Assembly Compile(string text)
public static Assembly Compile(string text, bool addDebugSymbols)
public static Assembly Compile(string text,
string outputFileName, bool addDebugSymbols)
public static Assembly Compile(string text, string outputFileName,
bool addDebugSymbols, ICompilerCallback compilerCallback)
// Methods for compiling multiple templates into one assembly
public static Assembly Compile(string[] fileList)
public static Assembly Compile(string[] fileList, string outputFileName)
public static Assembly Compile(string[] fileList,
string outputFileName, bool addDebugSymbols)
public static Assembly Compile(string[] fileList,
string outputFileName, bool addDebugSymbols,
ICompilerCallback compilerCallback)
上面的大多数参数都是不言自明的,最后一个参数除外,(Most of the parameters above are self-explanatory, with the exception of the last parameter,) compilerCallback
.模板编译器使用(. The Template compiler uses) ICompilerCallback
界面以报告编译尝试的结果.其接口定义如下:(interface to report the results of a compilation attempt. Its interface is defined as follows:)
public interface ICompilerCallback
{
void BeginCompile(CompilerArgs args);
void EndCompile(CompilerArgs args);
}
的(The) CompilerArgs
该类又定义为:(class, in turn, is defined as:)
public class CompilerArgs
{
private string _source;
private CompilerErrorCollection _errors = new CompilerErrorCollection();
public CompilerArgs(string source, CompilerErrorCollection errors)
{
_source = source;
if (errors != null)
_errors.AddRange(errors);
}
public string CompiledCode
{
get { return _source; }
}
public CompilerErrorCollection Errors
{
get { return _errors; }
}
}
如果要实现一个显示编译结果的GUI,则可以使用该界面.目前,由于我们不会在本文中制作GUI,因此可以放心地忽略它.(This interface can come in handy if you want to implement a GUI that displays the result of a compilation. For now, since we aren’t going to make a GUI in this article, we can safely ignore it.)
将单个模板编译到装配体(Compiling a Single Template into an Assembly)
将模板构建到已编译的程序集中很容易.一次致电(Building a template into a compiled assembly is easy. A single call to) TemplateCompiler.Compile()
做这项工作:(does the job:)
Dim templateAssembly As [Assembly] = TemplateCompiler.Compile(text)
…哪里(…where) text
是一个字符串变量,用于保存单个模板文件的内容.(is a string variable that holds the contents of a single template file.)
将多个模板文件编译成单个编译的程序集(Compiling Multiple Template Files into a Single Compiled Assembly)
将多个模板文件组合到一个程序集中就像编译一个模板一样容易:(Combining multiple template files into a single assembly is just as easy as compiling a single template:)
'Define the list of files
Dim files as String() = {"template1.tgt", "template2.tgt"}
'Build it!
Dim combinedAssembly As [Assembly] = TemplateCompiler.Compile(files)
请注意,该文件列表中的所有模板文件必须共享相同的语言.例如,如果其中一个模板是用C#编写的,那么其余模板也必须用C#编写.(Note that all of the template files in that list of files must share the same language. For example, if one of the templates is written in C#, then the rest of the templates have to be written in C# as well.)
共享属性(Shared Properties)
如前所述,TaHoGen允许您在单个共享对象上设置多个属性值,您可以将这些属性值传递给多个模板,这样您只需为所有模板设置一次属性即可.例如,这是将属性生成器模板插入到(As I mentioned earlier, TaHoGen allows you to set multiple property values onto a single, shared object that you can pass to multiple templates so that you will only have to set the properties for all of the templates once. For example, here is how you would insert the property generator template into the) FirstRegion
从上面的上一个示例中:(from the previous example above:)
PropertyTable properties = new PropertyTable();
properties["FirstRegion"] = new PropertyGenerator();
// The sample objects will automatically be initialized with
// the new property generator with the same values.
// Notice that we only have to set the properties once. Cool, eh?
SubTemplateSample sample1 = new SubTemplateSample(properties);
SubTemplateSample sample2 = new SubTemplateSample(properties);
. . .
// (This is where you would tell the sample templates to output the code, etc)
或者,您可以使用(Alternatively, you can use the) LoadProperties()
以相同方式分配属性值的方法:(method to assign the property values in the same manner:)
…
sample1.LoadProperties(properties);
sample2.LoadProperties(properties);
…
注意模板(Notice that templates) sample1
和(and) sample2
共享相同的属性集.我试图使库的设计尽可能简单,希望该功能有助于简化程序.(share the same set of properties. I tried to keep the design of the library as simple as possible, and hopefully this feature will help keep things simple.)
((()注意:(Note:)上面的示例假设您已将子模板示例和属性生成器模板都编译到了各自的程序集中,并在项目中对其进行了引用.如果您是从另一个模板中构建模板,则必须通过反射实例化这些模板,类似于我们在完整的VB.NET示例中所做的操作.)(The example above assumes that you’ve compiled both the subtemplate sample and the property generator templates into their respective assemblies and referenced them in your project. If you have built your templates from within another template, you will have to instantiate those templates through reflection, similar to what we did above in the complete VB.NET example.))
另一个要注意的重要事情是(Another important thing to note is that)的(the) PropertyTable
是类型安全的(is type safe).仅当该模板的属性类型与该属性表中存储的任何值相互兼容时,才会为模板分配属性值.否则,将忽略存储在属性表中的当前值. (您也可以将属性表连接到(. It will only assign a property value to a template if the property type of that template and the value of whatever is stored in that property table are compatible with each other. Otherwise, the current value stored in the property table will be ignored. (You can also connect a property table to a) PropertyGrid
直接控制和编辑其属性-但我将在下一篇文章中保留一些细节.)(control and edit its properties directly—but I’ll save that little detail for the next article.))
处理属性表值的更改(Handling Changes in Property Table Values)
此时,您可能想知道如果更改在两个或多个模板之间共享的属性表对象中的值,会发生什么.如果属性值在(At this point, you might be wondering what happens if you change a value in a property table object that is shared among two or more templates. If a property value changes in a) PropertyTable
,是否意味着您必须分配相同的(, does that mean that you have to assign that same) PropertyTable
所有这些模板再次?一点也不!一旦您更改了该特定商品的价值(to all of those templates all over again? Not at all! Once you change a value on that particular) PropertyTable
对象,则相同的更改将传播到该对象附加的所有模板.您只需设置一次该属性值.(object, that same change will be propagated to all templates that are attached to that object. You only have to set that property value once.)
单个模板,多个输出(Single Template, Multiple Outputs)
有时您可能希望将模板的输出发送到多个位置.假设您要发送的输出(There might be times when you might want to send the output of a template to more than one location. Suppose that you wanted to send the output of the) PropertyGenerator
调试窗口,控制台和外部文件的模板都同时显示.这是您的操作方式:(template to the debug window, the console, and an external file all at the same time. This is how you do it:)
// Set the property values as we did before
PropertyTable properties = new PropertyTable();
properties["Type"] = "string";
properties["Name"] = "MyProperty";
properties["ReadOnly"] = false;
// Instantiate the property generator and assign the property values
PropertyGenerator generator = new PropertyGenerator(properties);
DebugTarget debugOut = new DebugTarget();
ConsoleTarget consoleOut = new ConsoleTarget();
FileTarget fileOut = new FileTarget("output.txt", FileMode.Create);
// Connect the generator to its respective outputs
debugOut += generator;
consoleOut += generator;
fileOut += generator;
// Generate the output, and we’re done
debugOut.Write();
consoleOut.Write();
fileOut.Write();
…没有比这更容易的了. :)(…It doesn’t get any easier than that. :))
链接模板在一起(Chaining Templates Together)
您还可以将模板组合在一起以形成更复杂的模板.这是内置的一个简单但有用的示例(You can also combine templates together to form more complex templates. Here is a trivial, but useful example with the built-in) SimpleTextGenerator
模板:(template:)
SimpleTextGenerator hello = new SimpleTextGenerator("Hello, ");
SimpleTextGenerator world = new SimpleTextGenerator("World!");
TextGenerator helloWorld = hello + world;
// Say "Hello, World!" to the console
ConsoleTarget console = new ConsoleTarget();
console.Attach(helloWorld);
console.Write();
您甚至可以结合(You can even combine the) helloWorld
模板与另一个模板一起形成另一个复合模板.如您所见,使用此功能可以制作的模板绝对没有限制.剩下的我留给你想像.(template with another template to form another composite template. As you can see, there’s absolutely no limit to the templates you can make using this feature. The rest I leave to your imagination.)
如果您喜欢的.NET语言不支持运算符重载(If your favorite .NET language doesn’t support operator overloading)
另外,如果您使用的.NET语言不支持运算符重载(即VB.NET),则可以使用(Alternatively, if the .NET language you’re using does not support operator overloading (i.e., VB.NET), you can use the) TextGenerator.Combine
(
)
方法:(method instead:)
Dim helloWorld as TextGenerator = TextGenerator.Combine(hello, world)
在C#中,(In C#, the signature of the) Combine(
)
方法定义如下:(method is defined as follows:)
public static TextGenerator Combine(params TextGenerator[] generators);
无论您选择哪种方法(无论是运算符重载还是(No matter which method you choose (whether it is operator overloading, or the) Combine()
方法),两种方法都会产生相同的结果.(method), both methods will produce the same result.)
不支持的功能(Features That Are Not Supported)
继承属性(Inherits Attribute)
我选择不实施(I chose not to implement the) inherits=""
属性以保持简单.允许用户插入自己的自定义(attribute in order to keep things simple. Allowing the user to insert their own custom) TextGenerator
对象模型中派生的模板不必要地使引擎的CodeDom部分复杂化,我认为在这种特殊情况下继承提供的许多功能可以很容易地由其他方法(例如包含和委派)代替.(-derived template into the object model unnecessarily complicates the CodeDom portion of the engine, and I think that many of the features that inheritance affords in this particular case can be easily replaced by other methods, such as containment and delegation.)
CodeTemplateInfo对象(CodeTemplateInfo Object)
由于我没有(也不打算购买)(Since I do not have (nor intend to purchase) a) 史密斯(CodeSmith) 源代码许可证,我无法提供CodeSmith的Eric J. Smith对象模型中可能包含的任何内容.另一方面,虽然实施我们自己的自定义确实非常容易(source license, I can’t include anything that might be a part of Eric J. Smith’s object model from CodeSmith. On the other hand, although it’s really easy to implement our own custom) CodeTemplateInfo
对象,此时我看不到有任何立即使用.(object, I fail to see any immediate use for it at this point.)
未来将实现的功能(Features That Will Be Implemented in the Future)
我很想在TaHoGen中实现很多事情,但是我要么没有时间,要么我现在没有实现这些技能.其中包括:(There are a lot of things that I would love to implement in TaHoGen, but I either just don’t have the time, or, I just don’t have the skill to implement them at this point. Among these are:)
- 合并目标(Merge Targets).这是将模板的输出发送到现有源文件的区域的能力.到目前为止,我已经准备好了一个区域解析器的原型,但是我无法弄清楚如何构建将区域与源文本的其余部分分开的解析树.如果您有使用Spirit中的树木进行工作的经验,并且愿意为您做贡献,那么如果您能提供帮助,我将非常感谢! :)(. This is the ability to send the output of a template to a region of an existing source file. So far, I have a prototype for a region parser in place, but I can’t figure out how to build the parse tree that will split the regions from the rest of the source text. If you have any experience with working with trees in Spirit and would like to contribute, I would really appreciate it if you could help! :))
- 将以不同.NET语言编写的多个模板文件编译为单个程序集(Compiling Multiple Template Files Written in Different .NET Languages into a Single Assembly).在某个时候,我希望能够做到这一点,以便可以将任何两个模板(无论使用哪种.NET语言编写)组合到一个.NET程序集中.这将使模板具有更高的可重用性,因为如果您要将模板与其他不兼容的模板结合使用,则无需将其重写为另一种.NET语言.(. At some point, I want to be able to make it so that any two templates (regardless of the .NET language that they were written in) can be combined into a single .NET assembly. This will make templates much more reusable since you won’t have to rewrite them into another .NET language if you want to combine it with another incompatible template.)
- 我们自己的SchemaExplorer(Our Own SchemaExplorer). SchemaExplorer的使用需要Eric Smith的许可,因此在TaHoGen中使用它绝对是不可能的.我们将需要一个等效的开源软件,但这将需要一些时间来实现.(. The use of SchemaExplorer requires a license from Eric Smith, so using it in TaHoGen is definitely out of the question. We’re going to need an open source equivalent, but that will take some time to implement.)
永远不会实现的功能(Features That Will Never Be Implemented)
- 智能感知(Intellisense). TaHoGen是纯代码生成引擎,而不是文本编辑器.说够了. :)(. TaHoGen is a pure code generation engine, not a text editor. Enough said. :))
- 语法高亮(Syntax Highlighting). TaHoGen是纯代码生成引擎,而不是文本编辑器.说够了. :)(. TaHoGen is a pure code generation engine, not a text editor. Enough said. :))
兴趣点(Points of Interest)
我认为,毋庸置疑,像这样的引擎具有无限的潜在用途.您可以将此引擎插入ASP.NET页面并使用它来模拟母版页,也可以将其用作对象关系映射层的SQL生成器.可能性是无止境.我认为,此引擎(和CodeSmith)中的模板最好的部分是,它不会对您施加任何形式的方法.它使您可以决定代码的外观,而不是相反.最后,唯一的限制就是您的想象力.(I think that it goes without saying that an engine like this one has an infinite amount of potential uses. You can plug this engine into an ASP.NET page and use it to emulate Master Pages, or you can use it to act as an SQL generator for an object-relational mapping layer. The possibilities are endless. I think the best part about templates in this engine (and CodeSmith to some extent) is that it doesn’t impose any sort of methodology on you. It lets you decide what your code should look like, and not the other way around. In the end, the only limit is your imagination.)
特别感谢(Special Thanks)
我要感谢以下使这个项目成为可能的人:(I have to thank the following people who made this project possible:)
- 埃里克`史密斯(Eric Smith)-我必须感谢他编写CodeSmith,并让我对编写此版本的代码感到足够沮丧.需求确实是(重新)发明之母!(- I have to thank him for writing CodeSmith, and getting me sufficiently frustrated enough with it to write my own version. Necessity truly is the mother of (re)invention!)
- 来自developerfusion.uk的James Crowley(James Crowley from developerfusion.uk)-感谢您提醒我写这篇文章!(- Thanks for reminding me to write this article!)
- 康维尔(J. Conwell)-我从您的dotNetScript项目中汲取了很多想法.优秀作品!(- I drew a lot of my ideas from your dotNetScript project. Excellent work!)
- 马克`克利夫顿(Marc Clifton)-山顶上的那个大个子,以他撰写的文章的质量启发了我.继续努力,马克!(- The big man on the mountain top who has inspired me with the quality of articles that he has written. Keep up the good work, Marc!)
- 托尼
阿洛特(*Tony Allowatt*)–您的实施(*– Your implementation of the*)
PropertyBag` 确实使我很容易在多个模板之间重用属性值.谢谢,托尼!(really made it easy for me to reuse property values across multiple templates. Thanks, Tony!)
执照(License)
- 该库是根据GNU公共许可中定义的条款和条件许可的.(This library is licensed under the terms and conditions defined in the GNU Public License).您可以自由地将其用于非商业目的.如果您打算将其任何部分用于商业应用程序,(. You are free to use it for non-commercial purposes. If you are going to use any part of it in a commercial application,)联络我(contact me)我们可以解决一些问题.(and we can work something out.)
历史(History)
- 首次发布于2005年3月14日.(First published on 3/14/2005.)
许可
本文以及所有相关的源代码和文件均已获得The Code Project Open License (CPOL)的许可。
C++ C# VC7.1 .NET1.1 .NET2.0 .NET1.0 VS2005 Visual-Studio VS.NET2003 Dev 新闻 翻译