Создание текста во время выполнения с помощью текстовых шаблонов T4

Текстовые строки в приложении можно создавать во время выполнения с помощью текстовых шаблонов среды выполнения Visual Studio. Компьютер, на котором выполняется приложение, не должен иметь Visual Studio. Шаблоны среды выполнения иногда называются предварительно обработанными текстовыми шаблонами, так как во время компиляции шаблон создает код, выполняемый во время выполнения.

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

Например, следующий шаблон можно использовать в приложении, которое создает HTML-отчет.

<#@ template language="C#" #>
<html><body>
<h1>Sales for Previous Month</h2>
<table>
    <# for (int i = 1; i <= 10; i++)
       { #>
         <tr><td>Test name <#= i #> </td>
             <td>Test value <#= i * i #> </td> </tr>
    <# } #>
 </table>
This report is Company Confidential.
</body></html>

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

Использование шаблона в приложении упрощает просмотр конечной формы выходных данных, чем можно, например, длинной серии инструкций записи. Внесение изменений в форму выходных данных проще и надежнее.

Создание текстового шаблона во время выполнения в любом приложении

Создание шаблона текста во время выполнения

  1. В Обозреватель решений в контекстном меню проекта нажмите кнопку "Добавить>новый элемент".

  2. В диалоговом окне "Добавление нового элемента" выберите шаблон текста среды выполнения. (В visual Basic подстановка Общие элементы>.)

  3. Введите имя файла шаблона.

    Примечание.

    Имя файла шаблона будет использоваться в качестве имени класса в созданном коде. Поэтому он не должен иметь пробелы или знаки препинания.

  4. Нажмите кнопку Добавить.

    Создается новый файл с расширением .tt. Его свойство Custom Tool имеет значение TextTemplatingFilePreprocessor. Он содержит следующие строки:

    <#@ template language="C#" #>
    <#@ assembly name="System.Core" #>
    <#@ import namespace="System.Linq" #>
    <#@ import namespace="System.Text" #>
    <#@ import namespace="System.Collections.Generic" #>
    
  5. Добавьте ссылку на пакет NuGet System.CodeDom.

Преобразование существующего файла в шаблон времени выполнения

Хорошим способом создания шаблона является преобразование существующего примера выходных данных. Например, если приложение создаст HTML-файлы, можно начать с создания обычного HTML-файла. Убедитесь, что он работает правильно и что его внешний вид правильный. Затем включите его в проект Visual Studio и преобразуйте его в шаблон.

Преобразование существующего текстового файла в шаблон времени выполнения

  1. Включите файл в проект Visual Studio. В Обозреватель решений в контекстном меню проекта нажмите кнопку "Добавить>существующий элемент".

  2. Задайте свойству custom Tools файла значение TextTemplatingFilePreprocessor. В Обозреватель решений в контекстном меню файла выберите пункт "Свойства".

    Примечание.

    Если свойство уже задано, убедитесь, что это TextTemplatingFilePreprocessor , а не TextTemplatingFileGenerator. Это может произойти, если вы включаете файл, который уже имеет расширение .tt.

  3. Измените расширение имени файла на .tt. Хотя этот шаг является необязательным, он помогает избежать открытия файла в неправильном редакторе.

  4. Удалите все пробелы или знаки препинания из основной части имени файла. Например, "Моя веб-Page.tt" будет неверным, но "MyWebPage.tt" является правильным. Имя файла будет использоваться в качестве имени класса в созданном коде.

  5. Вставьте следующую строку в начале файла. Если вы работаете в проекте Visual Basic, замените "C#" на "VB".

    <#@ template language="C#" #>

  6. Добавьте ссылку на пакет NuGet System.CodeDom.

Содержимое шаблона времени выполнения

Директива шаблона

Сохраните первую строку шаблона, так как это было при создании файла:

<#@ template language="C#" #>

Параметр языка будет зависеть от языка проекта.

Простое содержимое

Измените TT-файл , чтобы он содержал текст, который требуется создать приложением. Например:

<html><body>
<h1>Sales for January</h2>
<!-- table to be inserted here -->
This report is Company Confidential.
</body></html>

Внедренный код программы

Можно вставить программный код между <# и #>. Например:

<table>
    <# for (int i = 1; i <= 10; i++)
       { #>
         <tr><td>Test name <#= i #> </td>
             <td>Test value <#= i * i #> </td> </tr>
    <# } #>
 </table>

Обратите внимание, что операторы вставляются между <# ... #> и выражениями вставляются между <#= ... #>. Дополнительные сведения см. в статье "Написание текстового шаблона T4".

Использование шаблона

Код, созданный из шаблона

При сохранении TT-файла создается дочерний .cs или .vb-файл . Чтобы просмотреть этот файл в Обозреватель решений, разверните узел TT-файла. В проекте Visual Basic сначала выберите "Показать все файлы" на панели инструментов Обозреватель решений.

Обратите внимание, что дочерний файл содержит частичный класс, содержащий метод.TransformText() Этот метод можно вызвать из приложения.

Создание текста во время выполнения

В коде приложения можно создать содержимое шаблона с помощью такого вызова:

MyWebPage page = new MyWebPage();
String pageContent = page.TransformText();
System.IO.File.WriteAllText("outputPage.html", pageContent);

Чтобы поместить созданный класс в определенное пространство имен, задайте свойство custom Tool Namespace файла текстового шаблона.

Отладка текстовых шаблонов среды выполнения

Отладка и тестирование текстовых шаблонов среды выполнения аналогично обычному коду.

Точку останова можно задать в текстовом шаблоне. Если вы запускаете приложение в режиме отладки из Visual Studio, вы можете выполнить шаги по коду и оценить выражения часов в обычном режиме.

Передача параметров в конструкторе

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

Например, можно создать отдельный файл MyWebPageCode.cs:

partial class MyWebPage
{
    private MyData m_data;
    public MyWebPage(MyData data) { this.m_data = data; }}

В файле шаблона MyWebPage.tt можно написать следующее:

<h2>Sales figures</h2>
<table>
<# foreach (MyDataItem item in m_data.Items)
   // m_data is declared in MyWebPageCode.cs
   { #>
      <tr><td> <#= item.Name #> </td>
          <td> <#= item.Value #> </td></tr>
<# } // end of foreach
#>
</table>

Чтобы использовать этот шаблон в приложении, выполните следующие действия.

MyData data = ...;
MyWebPage page = new MyWebPage(data);
String pageContent = page.TransformText();
System.IO.File.WriteAllText("outputPage.html", pageContent);

Параметры конструктора в Visual Basic

В Visual Basic содержится отдельный файл MyWebPageCode.vb :

Namespace My.Templates
  Partial Public Class MyWebPage
    Private m_data As MyData
    Public Sub New(ByVal data As MyData)
      m_data = data
    End Sub
  End Class
End Namespace

Файл шаблона может содержать:

<#@ template language="VB" #>
<html><body>
<h1>Sales for January</h2>
<table>
<#
    For Each item In m_data.Items
#>
    <tr><td>Test name <#= item.Name #> </td>
      <td>Test value <#= item.Value #> </td></tr>
<#
    Next
#>
</table>
This report is Company Confidential.
</body></html>

Шаблон может вызываться путем передачи параметра в конструкторе:

Dim data = New My.Templates.MyData
    ' Add data values here ....
Dim page = New My.Templates.MyWebPage(data)
Dim pageContent = page.TransformText()
System.IO.File.WriteAllText("outputPage.html", pageContent)

Передача данных в свойствах шаблона

Альтернативным способом передачи данных в шаблон является добавление общедоступных свойств в класс шаблона в определении частичного класса. Приложение может задать свойства перед вызовом TransformText().

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

Использование частичных классов для кода

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

Сборки и ссылки

Если вы хотите, чтобы код шаблона ссылаться на .NET или другую сборку, например System.Xml.dll, добавьте его в ссылки проекта обычным образом.

Если вы хотите импортировать пространство имен так же, как using оператор, это можно сделать с помощью директивы import :

<#@ import namespace="System.Xml" #>

Эти директивы должны быть помещены в начало файла сразу после директивы <#@template .

Общая папка.

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

<#@include file="CommonHeader.txt" #>

Включенное содержимое может содержать любую смесь кода программы и обычного текста, а также может содержать другие директивы и другие директивы.

Директива include может использоваться в любом месте текста файла шаблона или включенного файла.

Наследование между текстовыми шаблонами времени выполнения

Вы можете совместно использовать содержимое между шаблонами времени выполнения, написав шаблон базового класса, который может быть абстрактным. inherits Используйте параметр директивы <@#template#> для ссылки на другой класс шаблона среды выполнения.

Шаблон наследования: фрагменты в базовых методах

В приведенном ниже примере шаблон обратите внимание на следующие моменты:

  • Базовый класс SharedFragments определяет методы в блоках <#+ ... #>компонентов класса.

  • Базовый класс не содержит свободного текста. Вместо этого все текстовые блоки происходят внутри методов признаков класса.

  • Производный класс вызывает методы, определенные в SharedFragments.

  • Приложение вызывает TextTransform() метод производного класса, но не преобразует базовый класс SharedFragments.

  • Базовые и производные классы — текстовые шаблоны среды выполнения; То есть свойство Custom Tool имеет значение TextTemplatingFilePreprocessor.

SharedFragments.tt:

<#@ template language="C#" #>
<#+
protected void SharedText(int n)
{
#>
   Shared Text <#= n #>
<#+
}
// Insert more methods here if required.
#>

MyTextTemplate1.tt:

<#@ template language="C#" inherits="SharedFragments" #>
begin 1
   <# SharedText(2); #>
end 1

MyProgram.cs:

...
MyTextTemplate1 t1  = new MyTextTemplate1();
string result = t1.TransformText();
Console.WriteLine(result);

Результирующий результат:

begin 1
    Shared Text 2
end 1

Шаблон наследования: текст в базовом тексте

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

AbstractBaseTemplate1.tt:

<#@ template language="C#" #>

Here is the description for this derived template:
  <#= this.Description #>

Here is the fragment specific to this derived template:
<#
  this.PushIndent("  ");
  SpecificFragment(42);
  this.PopIndent();
#>
End of common template.
<#+
  // State set by derived class before calling TextTransform:
  protected string Description = "";
  // 'abstract' method to be defined in derived classes:
  protected virtual void SpecificFragment(int n) { }
#>

DerivedTemplate1.tt:

<#@ template language="C#" inherits="AbstractBaseTemplate1" #>
<#
  // Set the base template properties:
  base.Description = "Description for this derived class";

  // Run the base template:
  base.TransformText();

#>
End material for DerivedTemplate1.

<#+
// Provide a fragment specific to this derived template:

protected override void SpecificFragment(int n)
{
#>
   Specific to DerivedTemplate1 : <#= n #>
<#+
}
#>

Код приложения:

...
DerivedTemplate1 t1 = new DerivedTemplate1();
string result = t1.TransformText();
Console.WriteLine(result);

Результирующий результат:

Here is the description for this derived template:
  Description for this derived class

Here is the fragment specific to this derived template:
     Specific to DerivedTemplate1 : 42
End of common template.
End material for DerivedTemplate1.

Шаблоны времени разработки. Если вы хотите использовать шаблон для создания кода, который становится частью приложения, см. статью "Создание кода во время разработки" с помощью текстовых шаблонов T4.

Шаблоны времени выполнения можно использовать в любом приложении, где шаблоны и их содержимое определяются во время компиляции. Но если вы хотите написать расширение Visual Studio, которое создает текст из шаблонов, изменяющихся во время выполнения, см . статью "Вызов преобразования текста" в расширении VS.