Опубликован: 25.07.2012 | Уровень: специалист | Доступ: платный
Лекция 5:

Технология текстовых шаблонов T4

< Лекция 4 || Лекция 5: 12 || Лекция 6 >
Аннотация: Обсуждается генерация кода с применением шаблонов T4 в Visual Studio. Изучаются синтаксис шаблонов T4, принцип их работы, контроль отступов, применение моделей UML и DSL. Также рассматриваются возможности расширения технологии T4, плюсы и минусы ее применения.

Введение

Основные усилия программистов при реализации и сопровождении программного проекта уходят на расширение, модификацию и исправление программного кода. На первоначальную разработку программы затрачивается относительно немного времени. Поэтому разработчики стараются внедрять решения, которые удобны и эффективны для последующих модификаций и дополнений. Легкость разработки первоначальной версии приложения не является главным приоритетом.

Наглядным и удобным способом генерации кода является применение текстовых шаблонов. Техники применения шаблонов являются в большей степени декларативными способами генерации текста. Шаблон уже предоставляет макет кода в естественном для программиста виде. Неизменяемая часть кода уже заложена в шаблоне в том же самом виде, что и готовый сгенерированный код. С помощью специальных команд, инструкций, выполнения программного кода выводятся те части шаблона, которые не являются статическими, а меняются в зависимости от метаданных и настроек. Текстовые шаблоны позволяют упрощать генерацию кода. Если нужно изменить создаваемый код, то не потребуется разбираться в программе, генерирующей код, а достаточно изменить наглядный шаблон. Однако шаблоны эффективно применимы только тогда, когда повторяющийся код встречается часто во многих участках приложения, и его объем достаточно большой.

Технология T4 (Text Templating Transformation Toolkit) является инструментом генерации кода на основе шаблонов. Она включена в Visual Studio 2008 и Visual Studio 2010. А в Visual Studio 2005 она доступна при установке DSL (Domain-Specific Language) и GAT(Guidance Automation Toolkit). С помощью текстовых шаблонов можно сгенерировать все виды текстовой информации, так называемых артефактов. Это может быть программный код на C#, VB.Net, разметка HTML, запросы SQL, документация и любая другая текстовая информация. Технологию T4 можно применять как для генерации отдельных файлов, так и крупных приложений.

Создание файла шаблона

Для создания простого шаблона T4 надо выполнить следующие шаги:

  1. Создать проект в Visual Studio.
  2. Выбрать добавление нового пункта(файла) в проект.
  3. Для Visual Studio 2010 выбрать Text Template. Для Visual Studio 2008 и 2005 выбрать Text File и поменять расширение файла на ".tt".
  4. Дать название файлу (например, MyClass.tt) и добавить его в проект.
  5. Добавить текст шаблона в файл. Например, такой:
    <#@ template debug="false" language="C#" #>
    <#@ output extension=".cs" #>
    <# string[] vars = new string [] {"A", "B", "C"}; #>
    class MyClass {
    <# 
      foreach (string variable in vars)
      { #>
      private int <#= variable #> = 0;
    <# } #>
    }
          
  6. Сохранить файл.

После этого шага под файлом шаблона появится файл кода на C#, как на следующем рисунке.

Файл кода, сгенерированного на основе шаблона T4

Рис. 4.1. Файл кода, сгенерированного на основе шаблона T4

Мы уже рассматривали этот пример во второй лекции. Если посмотрим на код сгенерированного файла MyClass.cs, то он будет таким.

class MyClass {
  private int A = 0; 
  private int B = 0;
  private int C = 0;
}
    

Синтаксис T4

Шаблонные файлы имеют синтаксис, похожий на страницы ASP.Net. Встроенный редактор T4 не выполняет даже простейшую подсветку синтаксиса. Также не работает IntelliSense. То есть редактор T4 в Visual Studio больше напоминает простой текстовый редактор. Однако есть продукты сторонних производителей с большими возможностями, которые в основном хоть и платные, но имеют урезанные бесплатные версии. Например, есть редактор Т4 от Tangible Engineering.

В шаблонах T4 применяются следующие виды блоков: <#@ #> - для директив, <# #> - для блоков кода, <#+ #> - для блоков вспомогательных методов, <#= #> - блоков выражений. Все, что не входит в вышеперечисленные блоки, считается блоками текста. Они не обрамляются никакими тегами. Что мы видим в блоках текста, то будет и отображено без изменений в сгенерированном коде.

Директивы являются элементами, которые указывают, как именно должен обрабатываться шаблон. Управляющими блоками считаются блоки кода, вспомогательных методов, а также блоки выражений. Именно управляющие блоки являются программным кодом, который манипулирует участками текста и осуществляет вывод в результат работы шаблона.

Блоки кода

Блоки кода вида <#...#> содержат код на C# или Visual Basic. В ходе своей работы этот код может выводить текст в файл результата шаблона.

public class MyClass
{
<#
for (int i=0;i < 2;i++)
{
#>
  public void DoAction()
  {
  }
<#
}
#>
}
        
Пример 4.1.

Результатом работы вышеуказанного шаблона будет следующий код:

public class MyClass
{
  public void DoAction()
  {
  }
  public void DoAction()
  {
  }
}
        

Этот сгенерированный код некорректен, так как в классе содержатся два метода с одним именем и сигнатурой. Но сейчас это не имеет большого значения, важен сам факт вывода кода.

Блоки выражений

Блоки выражений <#=...#> служат для записи результатов выражений непосредственно в текст.

public class MyClass
{
<#
for (int i=0;i < 2;i++)
{
#>
  public void DoAction<#=i#>()
  {
  }
<#
}
#>
}
        
Пример 4.2.

Здесь выражение <#=i#> подставляет значение i в текст шаблона. Значение i меняется циклически. Результатом работы шаблона будет следующий код.

public class MyClass
{
  public void DoAction0()
  {
  }
  public void DoAction1()
  {
  }
}
        

Разумеется, выражение может выглядеть гораздо сложнее. Альтернативой блокам выражений является применение методов Write и WriteLine в блоке кода.

public class MyClass
{
<#
for (int i=0;i < 2;i++)
{
  WriteLine("\tpublic void DoAction" + i + "()");
#>
  {
  }
<#
}
#>
}
        
Пример 4.3.

Здесь символ "\t" означает табуляцию. Результат будет тем же, что и в предыдущем примере.

Блоки вспомогательных методов

Блоки вида <#+…#> позволяют добавлять вспомогательные методы (функции) в шаблон. Они могут впоследствии вызываться изнутри шаблона, где это необходимо. Рассмотрим следующий пример.

public class MyClass
{
<#
for (int i=0;i < 2;i++)
{
#>
  public void <#= CreateFunctionName(i)#>()
  {
  }
<#
}
#>
}
<#+ public string CreateFunctionName(int counter)
{
  string result = "";
  switch (counter)
  {
    case 0:
      result = "DoActionOne";
      break;
    case 1:
      result = "DoActionTwo";
      break;
  }
  return result;
}
#>
        
Пример 4.4.

В цикле вызывается метод CreateFunctionName. Он объявляется в блоке вспомогательных выражений между тегами <#+ и #>.

Вспомогательные методы обязательно должны находиться в конце шаблона. Блок вспомогательных методов может содержать также блоки выражений, кода, текста - как и в самом шаблоне. Результатом работы примера будет следующее:

public class MyClass
{
  public void DoActionOne()
  {
  }
  public void DoActionTwo()
  {
  }
}
        

Директивы

Директивы служат для установки правил обработки конкретного файла шаблона. Синтаксис директив имеет следующий формат:

<#@ ИмяДирективы [ИмяАттрибута = "ЗначениеАттрибута"] ... #>
        

Каждая из директив имеет свое предназначение. Имя директивы указывается один раз в самом начале и принимает одно из следующих значений:

  • Template,
  • Parameter,
  • Output,
  • Assembly,
  • Import,
  • Include.

Также можно создавать пользовательские директивы. Секция [ИмяАттрибута = "ЗначениеАттрибута"] может указываться один и более раз. Давайте рассмотрим директивы поподробнее.

Директива template

Директива Template указывает среде правила обработки файла шаблона и имеет следующий формат:

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

Все атрибуты являются необязательными. Параметр language указывает язык, на котором пишется код в шаблоне. Может принимать значения "C#" или "VB". Атрибут compilerOptions определяет условия компиляции. Атрибут culture указывает на культурную среду. Атрибут debug указывает, нужна отладка или нет. С помощью атрибута inherits можно указать, что код файла шаблона может наследоваться от другого класса, который, в свою очередь, тоже может быть сгенерирован из другого шаблона.

Установка атрибута hostspecific в значение true позволяет коду шаблона получить доступ к API Visual Studio. В коде шаблона становится возможным указывать относительный путь к файлам проекта, а не абсолютный. Также можно получить доступ из шаблона к свойствам класса Host, например, к дате и времени:

<#= Host.CurrentDateTime #>
            

Рассмотрим следующую директиву

<#@ template language="С#" hostspecific="true" debug="true" #>
            

Эта директива указывает, что код шаблона будет реализован на языке C# и нужен доступ к API Visual Studio. Включается отладчик для поиска и устранения ошибок в шаблоне.

Директива parameter

Директива parameter позволяет указывать параметры, которые могут передаваться в шаблон извне, и имеет следующий формат:

<#@ parameter type="ИмяТипа" name="Имя параметра" #>
            

Директива parameter доступна только начиная с Visual Studio 2010/.NET 4. Однако, во всех версиях Visual Studio можно передавать параметры в шаблон добавлением значений в класс CallContext.

Директива parameter имеет обязательные атрибуты type и name. Атрибут type передает название типа параметра. При этом должно указываться полное имя типа, например System.string, а не string. Этот пример передает строковый параметр TableName:

<#@ parameter type="System.string" name="TableName"#>
            

После включения параметров их можно использовать внутри шаблона как переменные.

Директива output

Директива output позволяет указать расширение и кодировку выходного файла. Расширение можно указать любое, по умолчанию принимается значение ".cs". Кодировка может принимать такие значения, как "utf-8", "us-ascii", "utf-32" и т.д. Имеет следующий формат.

<#@ output extension=".расширение" [encoding="обозначениеКодировки"] #>
            
Директива assembly

Директива assembly позволяет загрузить сборку и применять классы и методы этой сборки в шаблоне. Если нужна сборка из состава .Net, то можно просто указать имя сборки. Для других сборок надо указать полный путь к файлу сборки. Формат директивы assembly следующий:

<#@ assembly name="[ИмяСборки|ПутьКФайлу]" #>
            
Директива import

Формат директивы import:

<#@ import namespace="ПространствоИмен" #>
            

Эта директива позволяет импортировать пространства имен. В коде шаблона можно будет использовать простые имена классов вместо полных имен.

Директива include

Формат директивы include:

<#@ include file="filePath" #>
            

Эта директива позволяет включить в шаблон содержимое другого файла. Включаемый файл может содержать как шаблонный текст, так и команды обработки. Также он может иметь расширение, отличное от ".tt", например ".txt" или ".t4". Если в двух файлах шаблона есть схожие участки, можно в отдельном файле хранить общую часть шаблонов. А в оригинальных файлах директивы включения надо поставить вместо повторяющихся участков кода. Позже, при необходимости можно менять только содержимое включаемого файла.

< Лекция 4 || Лекция 5: 12 || Лекция 6 >
Дмитрий Клочков
Дмитрий Клочков
Россия, Рубцовск
Волков Олег
Волков Олег
Украина, Днепропетровск