Россия, Рубцовск |
Технология текстовых шаблонов T4
Пример чтения из XML-файла
В шаблонах можно считывать метаданные из XML-файлов и генерировать файлы. Допустим, нам надо создать код объявлений констант. Создадим файл variables.xml и добавим его в проект, заполнив таким содержимым:
<?xml version="1.0" encoding="utf-8" ?> <constants> <constant name="A" value="1"/> <constant name="B" value="2"/> <constant name="C" value="3"/> <constant name="D" value="4"/> </constants>Пример 4.4.
Следующий шаблон считывает данные из файла метаданных и генерирует код:
<#@ template debug="false" hostspecific="true" language="C#" #> <#@ output extension=".cs" #> <#@ assembly name="System.Xml" #> <#@ import namespace="System.Xml" #> <# XmlDocument reader = new XmlDocument(); reader.Load(this.Host.ResolvePath("variables.xml")); XmlElement elem =reader.DocumentElement; for (int i = 0; i < elem.ChildNodes.Count; i++) { XmlElement elemField = (XmlElement)elem.ChildNodes[i]; #> const int <#=elemField.GetAttribute("name")#> = <#=elemField.GetAttribute("value")#>; <# } #>Пример 4.5.
Установка параметра hostspecific в значение true позволяет применить выражение this.Host.ResolvePath("variables.xml"). Директива assembly дает возможность использовать в шаблоне классы для работы с XML. Директива import позволяет использовать сокращенные имена классов, например можно писать XmlDocument вместо System.Xml.XmlDocument. Параметр extension директивы output устанавливает расширение выходного файла. Также внутри шаблонного текста используются блоки выражений <#= #>. Результат работы шаблона будет таким:
const int A = 1; const int B = 2; const int C = 3; const int D = 4;
Как работают шаблоны T4
Механизм T4 выполняет генерацию в два шага. На первом шаге происходит разбор шаблона и генерируется класс GeneratedTextTransformation, который содержит метод TransformText. Выполняется компиляция этого класса в сборку. На втором шаге создается объект данного класса и запускается метод TransformText, который и формирует текст. Другими словами, на первом шаге из шаблона генерируется программа, на втором шаге эта программа запускается и генерирует выходной файл.
Контроль отступов в генерируемом коде
При генерации кода возникает необходимость автоматической установки в нем отступов. В следующем примере в первых двух строках и последней строке нет отступа, а в начало остальных строк вставлена табуляция.
public class MyClass { public void DoAction0() { } public void DoAction1() { } }
Методы PushIndent, PopIndent, ClearIndent позволяют гибко контролировать отступы в генерируемом тексте. Метод PushIndent устанавливает отступ для следующих за ним строк. К началу каждой строки генерируемого текста добавляется символьная строка. Ее содержимое передается в этот метод. Отступом может быть не только табуляция, но и любая последовательность символов. Метод PushIndent может вызываться много раз. После каждого вызова метода строка символов добавляется в стек. Метод PopIndent убирает последний добавленный отступ. Метод ClearIndent очищает стек отступов.
Рассмотрим пример, при котором нам надо отобразить следующую таблицу из четырех ячеек в формате HTML:
<table> <tr> <td>1</td> <td>2</td> </tr> <tr> <td>3</td> <td>4</td> </tr> </table>
Шаблон T4, формирующий HTML-разметку:
<#@ template language="C#" #> <#@ output extension=".html" #> <# WriteLine("<table>"); PushIndent("\t"); WriteLine("<tr>"); PushIndent("\t"); WriteLine("<td>1</td>"); WriteLine("<td>2</td>"); PopIndent(); WriteLine("</tr>"); WriteLine("<tr>"); PushIndent("\t"); WriteLine("<td>3</td>"); WriteLine("<td>4</td>"); PopIndent(); WriteLine("</tr>"); ClearIndent(); WriteLine("</table>"); #>Пример 4.6.
Обратите внимание, что нет необходимости добавлять табуляцию в каждом вызове метода WriteLine. Табуляция добавляется в стек отступов в методе PushIndent.
Генерация текста из моделей DSL и UML
Модель генерируемой области может быть заложена в базе данных, текстовом файле, XML-файле, файле Visio, а также в диаграммах UML и языках DSL. Чтение данных из XML-файла мы уже рассматривали. Так же достаточно стандартно читаются данные из текстового файла и баз данных. А сейчас мы кратко рассмотрим особенности генерации текста в T4 из моделей UML и DSL.
Генерация кода из модели UML представляется очень удобным решением. На практике в большинстве случаев модель UML плохо связана с программным кодом, документацией, и т.д. Являясь средством проектирования и визуального представления, диаграммы UML как бы живут своей жизнью. При внедрении изменений часто изменения вносятся отдельно в диаграмму и отдельно в программный код. Применение технологии T4 для генерации файлов из модели UML может оказаться эффективным. При изменении схемы UML надо только заново сгенерировать файлы.
Существуют три способа генерации файлов из модели UML с применением шаблонов T4:
- Генерация файлов из меню команд. Создается команда Visual Studio, которая запускается для моделей UML.
- Генерация файлов из приложения. Создается приложение, которое читает модели UML и генерирует файлы.
- Генерация времени построения. Файлы генерируются из модели UML внутри проекта Visual Studio.
DSL (Domain-Specific Language), или язык предметной области, является нотацией, чаще всего графической, разработанной для конкретных, определенных целей. Для каждой специфической задачи создается язык DSL, учитывающий ее особенности. Эта технология представляет собой противоположность языкам вроде UML, которые являются языками общего назначения, "на все случаи жизни".
DSL доступен в Visual Studio 2010 через меню добавления нового проекта. Технология DSL позволяет создавать описание предметной области. Создавая шаблоны T4 и передавая метаданные предметной области можно генерировать разного рода тексты, программы и т.д.
Возможности расширения функциональности T4
Есть несколько способов расширить функциональность T4:
- Создание собственного хоста в Visual Studio, своего рода надстройку над ним. Шаблоны T4 должны выполняться внутри некоего хоста, являющегося программой. Visual Studio уже предоставляет хост по умолчанию, однако вы можете создать свой и расширить возможности генерации кода.
- Предоставлением нового базового класса могут быть расширены возможности шаблонов T4. Во время генерации кода шаблон вызывает методы из этого класса, чтобы собирать информацию, которая будет использована при генерации кода.
- Создание пользовательских директив и применение их в шаблоне.
Плюсы применения шаблонов T4
- Можно весьма эффективно расширять возможности T4, добавляя пользовательские хосты, директивы. Этими способами видоизменяется или усовершенствуется поведение T4.
- Генерируется не только код, но и любая текстовая информация.
- Шаблоны наглядны и просты для понимания. Они эффективны при большом объеме не меняющегося стандартного кода и малом числе настраиваемых параметров.
Минусы применения шаблонов T4
- Один шаблон изначально предназначен для создания только одного файла, хотя это ограничение можно и обойти.
- Шаблоны T4 поддерживают только два языка - C# и Visual Basic.
- Шаблоны могут оказаться малоэффективными, если в генерируемом коде мало стандартных участков и много параметров, от которых зависит содержимое сгенерированного кода. В некоторых подобных случаях шаблоны по сложности могут превзойти программы, генерирующие код путем соединения строк текста.
- Программисты сильно зависят от того, как именно Microsoft будет поддерживать технологию T4 в будущих версиях Visual Studio.
Заключение
Шаблон T4 является, по сути дела, текстом. И его можно хранить в файле или базе данных. Используя набор разных шаблонов, и применяя включение шаблонов, можно строить сложные схемы генерации кода. Технология Т4 может расширяться путем добавления новых возможностей. Наиболее эффективно применение шаблонов в тех случаях, когда большой объем сгенерированного кода остается неизменным и в генератор передается мало параметров.
Технология T4 имеет множество ограничений, которые, однако, могут быть преодолены ее встроенной расширяемостью и разнообразными приемами. Поведение директив может отличаться на разных хостах, а также может быть изменено созданием пользовательских хостов. Также, синтаксис шаблонов T4 могут расширять пользовательские директивы.