Главная страница   /   3.5. Использование T4 с Visual Studio (Метапрограммирование в .NET

Метапрограммирование в .NET

Метапрограммирование в .NET

Кевин Хазард

3.5. Использование T4 с Visual Studio

T4 интегрируется в Visual Studio без особых сложностей. Если вы добавляете .tt файлы практически в любой тип проекта, выходные файлы автоматически генерируются обратно в проект. Что может быть проще? В этом разделе рассматривается, как Т4 и Visual Studio работают вместе. В процессе вы выйдете за рамки основ, которые вы узнали в предыдущем разделе, чтобы понять, как использовать метапрограммирование с Т4, чтобы решить некоторые трудные проблемы.

Как T4 использует точку расширения генератора одного файла

Отношение T4 к Visual Studio не является специальным из перспективы интегрированной среды разработки (IDE). Так называемая точка расширения генератора одного файла в системе MSBUILD используется для решения различных общих задач генерации кода, в том числе преобразований T4. Для поддержки этой возможности в целом, формат файла проекта, используемый Visual Studio, включает свойство исходного файла CustomTool, показанное на рисунке 3-2. Обратите внимание, что файл шаблона отмечен так, чтобы его не создавали и не копировали в выходную директорию. Для обработки файла будет запущен пользовательский инструмент TextTemplatingFileGenerator. Этот пользовательский инструмент является хостом T4, предназначенным для Visual Studio.

Рисунок 3-2: Окна Solution Explorer и Properties в Visual Studio 2010 показывают свойства файла для шаблона T4

Обратите особое внимание на значение свойства CustomTool. Значение было автоматически установлено на TextTemplatingFileGenerator, когда .tt файл был добавлен в проект.

Для других типов файлов Visual Studio существуют различные значения свойства CustomTool. Вот краткий список наиболее распространенных генераторов кода, который вы найдете, если просмотрите реестр на компьютере, где была установлена Visual Studio:

  • DataServiceClientGenerator: создает клиентов доступа OData
  • EntityModelCodeGenerator: создает классы из EF моделей
  • MSLinqToSQLGenerator: создает классы из LinqToSql моделей
  • ResXFileCodeGenerator: создает классы из .resx файлов
  • WCFProxyGenerator: создает прокси классы веб сервиса

Если вы работали с .NET, вы, возможно, видели некоторые из этих значений, указанных в свойстве CustomTool, и удивлялись, как они туда попали. К сожалению, ответ на этот вопрос не может быть найден в хорошем, чистом окне конфигурации Visual Studio. Он похоронен в реестре рядом с генератором имен, показанном в предыдущем списке. Например, файлы с расширением .edmx связаны с EntityModelCodeGenerator. А файлы шаблона Т4 с расширением .tt связаны с TextTemplatingFileGenerator, как показано на рисунке 3-2. Эти зарегистрированные связи говорят Visual Studio, как установить значение свойства CustomTool,всякий раз когда добавляется файл.

После того как была установлена связь инструментов, другие значения реестра помогают Visual Studio найти сборки, которые выполняют генерацию кода. Для преобразований T4 генератор одного файла находится в классе с именем TemplatedCodeGenerator. Каждый из перечисленных выше генераторов кода следует этому же паттерну. Каждый из них имеет одну или несколько связей для файлов, зарегистрированных для них, чтобы убедиться, что в файл проекта установлено правильное свойство CustomTool. И у каждого из них есть зарегистрированная сборка, которая делает шаг генерации кода по созданию подчиненных файлов для каждого исходного файла.

Понимание точки расширения генератора одного файла в Visual Studio заключается в том, что T4 интегрируется в Visual Studio, используя ту же стандартную точку расширения генератора одного файла, которую используют многие другие инструменты. Кроме этого, Visual Studio не знает, что происходит внутри шаблона T4. На момент написания книги, Visual Studio не предоставляла специализированную IDE или IntelliSense поддержку шаблону T4. Но есть несколько отличных сторонних инструментов, которые делают это.

Создание шаблона T4 из Visual Studio

Теперь, когда вы знаете немного о том, как T4 подключается к Visual Studio для генерации кода во время разработки, давайте немного с ним поиграем. Добавьте шаблон T4 в любой проект, щелкнув правой кнопкой мыши по имени проекта в Solution Explorer и выбрав пункт меню Add New Item. Появится диалоговое окно, показанное на рисунке 3-3. Обратите внимание, как было использовано слово template в текстовое поле поиска, чтобы найти типы элементов T4. Чтобы добавить шаблон, выберите тот, что называется Text Template, назовите его и нажмите кнопку Add.

Рисунок 3-3: Добавление шаблона в проект в Visual Studio 2010

С левой стороны диалогового окна Add New Item обратите внимание на список Installed Templates. Это не шаблоны T4, хотя можно предположить, что в некоторых будущих версиях Visual Studio T4, вероятно, сможет быть использован функцией Add New Item для создания файлов проекта динамически. Безусловно, ничто не мешает третьим лицам расширять Visual Studio таким образом.

Среди шаблонов файлов Visual Studio есть два шаблона, направленные на T4, которые вы можете использовать для добавления текстовых шаблонов к вашим проектам. Вместо того чтобы искать их среди всех категорий Installed Templates в списке, показанном на рисунке 3-2, вы можете использовать функцию поиска шаблонов Visual Studio 2010, чтобы найти их. Введите слово template в окне поиска для диалогового окна Add New Item и нажмите Enter. Рисунок 3-3 показывает результаты запроса: два варианта T4, которые доступны сегодня в Visual Studio 2010:

  • Preprocessed Text Template: шаблон T4 исполнения при запуске
  • Text Template: шаблон T4 исполнения при написании

Вы можете считать Preprocessed Text Template таким шаблоном, который не требует запуска Visual Studio. На самом деле, в документации Visual Studio они также называются Run-Time Text Templates (Текстовые шаблоны исполнения при запуске). Мы подробно покрываем Preprocessed Templates в следующем разделе. Другой вид шаблонов – это так называемый Text Template (Текстовый шаблон), также обозначенный в документации Visual Studio как Design-Time Text Template (Текстовый шаблон исполнения при написании). Чтобы создать шаблон, который компилируется и преобразовывается во время процесса построения вашего решения, Text Template является правильным выбором. В любом открытом проекте добавьте Text Template с именем DesignTimeFun.tt и нажмите кнопку Add в диалоговом окне Add New Item. Когда Visual Studio создаст новый .tt файл из своего собственного шаблона, он будет содержать только эти две директивы:

<#@ template debug="false"
	hostspecific="false" language="C#" #>
<#@ output extension=".txt" #>

Запомните, что все директивы T4 ограничены последовательностью символов <#@ и #>, как было показано ранее. Если Вы работаете с проектом Visual Basic, атрибут language для директивы template первоначально будет установлен на "VB" вместо "C#", как показано на рисунке. Важно понимать, что выбор языка в директиве template определяет язык управления для шаблона, а не тип файла выходных данных шаблона.

Язык управления T4 vs. выходные данные

Т4 в более общем смысле считается текстовым генератором, а не генератором кода. Вы можете использовать Visual Basic для создания C# или F#, Python или XML. T4 преобразования также выполняются вне контекста нормальной, контролируемой Visual Studio компиляции, так что если вы работаете над C# проектом и хотите использовать Visual Basic в качестве языка управления внутри шаблонов, это совершенно нормально.

Подробнее о директиве template

Все шаблоны T4 должны включать в себя директиву <#@template#>. Для версий T4, работающих с Visual Studio 2010 и более поздних, атрибут language не требуется и будет по умолчанию установлен на "C#", если не указано иное. Более ранние реализации T4 могут потребовать атрибут language для директивы template, так что проверьте документацию по версии, которую вы используете. За кулисами используется CodeDOM для компиляции шаблонов T4, поэтому в зависимости от установленных провайдеров CodeDOM на вашем компьютере, вы можете выбрать конкретную версию компилятора управляющего языка для T4, установив для атрибута language конкретное значение, например, "C#2.0". Проверьте документацию, которая поставляется с Visual Studio, чтобы точно знать, какие языки поддерживаются. В общем, указание "C#" или "VB" для атрибута language является безопасным выбором, потому что таким образом будут выбраны компиляторы по умолчанию для языков, установленных на вашем компьютере.

Также не требуются атрибуты debug и hostspecific для T4 директивы template, включенные файловым шаблоном Visual Studio. Но так как вы, вероятно, захотите вносить изменения в эти атрибуты время от времени, файловый шаблон Visual Studio, который создает начальный .tt файл, включает их в элемент template, используя их значения по умолчанию. Вы можете оставить атрибуты debug и hostspecific в директиве template, когда они появляются, или удалить их, если хотите.

Этот текст не претендует на то, чтобы дать исчерпывающую информацию по всем вариантам директив T4, но есть несколько других атрибутов директивы template, которые стоит упомянуть. Первый – это атрибут compilerOptions. Мы упоминали, что T4 внутренне использует CodeDOM, чтобы создать класс из шаблона, скомпилировать и вызвать его. Во время процесса компиляции вы можете передать параметры компилятору, как вы бы делали в командной строке, если бы вызывали компилятор вручную. Если языком управления T4 является C#, то любой из параметров командной строки для компилятора CSC.EXE может быть передан при помощи атрибута compilerOptions директивы template следующим образом:

<#@ template language="C#" compilerOptions="warnaserror+" #>

Этот пример поднимет любые предупреждения, сгенерированные во время компиляции, до ошибок, и компиляция не пройдет. Компилятор Visual Basic также поддерживает опцию warnaserror+, поэтому то же значение compilerOptions может быть использовано, если Visual Basic является языком управления T4.

Другим атрибутом директивы template, который стоит отметить, является culture. Если этот атрибут указан, то поток, который выполняет преобразование, заранее будет установлен на назначенный язык и региональные параметры. Значение строки должно придерживаться стандарта из двухсимвольного кода ISO 639 (язык), за которым следует дефис, и двухсимвольного кода ISO 3166 (региональные параметры). Например, чтобы убедиться, что используются настройки глобализации для бразильского португальского при генерации выходных данных шаблона T4, может быть использована следующая директива:

<#@ template language="C#" culture="pt-BR" #>

Если язык и региональные настройки включены в шаблон таким способом, то стандартный хост T4, используемый Visual Studio, устанавливает язык и региональные настройки обрабатываемого потока на указанное значение до того, как произойдет трансформация. Если значения нет, преобразование происходит с использованием имеющегося значения по умолчанию Windows.

Использование директивы output

Выходные файлы шаблонов исполнения во время написания всегда именуются тем же именем корневого файла, что и связанные с ними файлы шаблона. Чтобы задать другое расширение, вы должны использовать директиву <#@output#>. Интересно, что если вам не удалось указать директиву output, расширение файла, который будет выпущен, всегда будет .cs, независимо от того, является ли C# языком включающего проекта.

Поскольку шаблон исполнения при написании может генерировать любые выходные текстовые данные во время компиляции, вы должны использовать директиву output для того, чтобы указать расширение создаваемого файла. Программисты, которые только начинают работать с Т4, иногда предполагают, что указанное расширение влияет на выходные данные другими способами. Но это не так. T4 не делает никакой специальной обработки на основе назначенного output extension. Вы можете создать из шаблона файл с традиционным T-SQL расширением файла вот таким образом:

<#@ output extension=".sql" #>

Но добавление .sql расширения к имени выходного файла не вызывает никакой специальной логики T-SQL преобразования в ходе процесса. T4 не имеет никакого кода конкретного языка для любого вида текста, который он может создать. Что выпускается – это чисто функция управляющих и текстовых блоков, которые включены в каждый шаблон.

Директива output также позволяет установить кодировку выходного файла с помощью дополнительного атрибута encoding. Например, чтобы создать Python файл с кодировкой UTF-16, может быть использована следующая директива в шаблоне T4:

<#@ output extension=".py" encoding="utf-16" #>

Кодировка по умолчанию – это UTF-8, которая является Unicode кодировкой, имеющей обратную совместимость с ASCII. Единственная реальная проблема с UTF-8 заключается в том, что она многобайтовая, а это означает, что некоторые последовательности символов длиннее, чем другие. То есть к файлам с UTF-8 кодировкой нельзя получить произвольный доступ, в отличие от тех, что кодируются с использованием форматов символов фиксированной ширины, таких как UTF-16 и UTF-32. Если вы не работаете с огромными входными файлами или если вы планируете обрабатывать выходные файлы последовательно, UTF-8, как правило, является отличным выбором для кодирования файлов, потому что она также не зависит от Byte Order Marking (BOM). В дополнение к кодировкам, упомянутым выше, Т4 также поддерживает ASCII, UTF-16BE (Big-Endian) и UTF-7.

Использование T4 для динамической генерации Visual Basic

Используя то, что вы уже знаете, давайте немного поиграем. Давайте создадим консольное приложение, используя Visual Basic, и включим шаблон T4, который использует C# в качестве языка управления и создает Visual Basic файл для компиляции в качестве основного модуля.

  • Создайте консольное приложение Visual Basic и измените файл Module1.vb для вывода строки на консоль:
ModuleModule1
	SubMain()
		Console.WriteLine("Hello,world!")
	EndSub
EndModule
  • Скомпилируйте и запустите приложение, чтобы убедиться, что он работает так, как вы и ожидали, выводя фразу "Hello, World!" в окно консоли.
  • Замените файл Module1.vb шаблоном. Кликните правой кнопкой мыши по консольному проекту в Solution Explorer и выберите Add New Item или выберите Add New Item из меню Project в Visual Studio. Появится диалоговое окно Add New Item, показанное ранее на рисунке 3-3.
  • Найдите T4 шаблоны, набрав template в поле поиска в правом верхнем углу и нажав Enter.
  • По результатам поиска выберите файловый шаблон Visual Studio, который называется Text Template.
  • Назовите новый файл DynamicModule1.tt и нажмите кнопку Add, чтобы добавить файл в проект. Обратите внимание, что в проект был добавлен новый текстовый шаблон T4 с именем DynamicModule1.tt.
  • Откройте .tt файл и введите код шаблона, как показано в следующем листинге. Обратите внимание две вещи: язык управления шаблона – это C#, хотя данный проект является типа Visual Basic; и extension в директиве output также предназначен для Visual Basic.
Листинг 3-14: DynamicModule1.tt
<#@ template language="C#" #>
<#@ output extension=".vb" #>
<#
	string msg = "Hello, world!";
	int n = 0;
#>
Module Module1
	Sub Main()
		Console.WriteLine("<# while (n < msg.Length)
			Write(msg[n++].ToString());
		#>")
	End Sub
End Module
  • Сохраните файл DynamicModule1.tt и посмотрите, какие ошибки компиляции есть в окне выхода Visual Studio. Если вы сделали ошибку, которая вызвала ошибку компиляции, дважды щелкните по ошибке в окне выхода: обратите внимание, что курсор переместился на место с ошибкой в шаблоне.
  • Для проектов Visual Basic, в отличие от проектов C#, выходные файлы для текстовых шаблонов T4 не отображаются по умолчанию в окне Solution Explorer. Вы должны нажать кнопку Show All Files в верхней части окна Solution Explorer, чтобы иметь возможность увидеть файл DynamicModule1.vb, который был создан шаблоном. Как только вы сделаете это, откройте выходной файл и посмотрите на него. Вы его узнаете? Да, он точно такой же, как Visual Basic код, указанный в пункте 1 этой задачи.
  • Перед компиляцией всего проекта снова, есть одна маленькая проблема, которая должна быть решена. В своем нынешнем виде ваш проект состоит из двух идентичных модулей с именем Module1. Компилятору Visual Basic это совсем не нравится. Кликните правой кнопкой мыши по файлу Moldule1.vb в Solution Explorer и выберите Delete из контекстного меню.
  • Скомпилируйте и снова запустите проект. Вы увидите, что результат будет точно таким же, как и результат, показанный в шаге 2 этой задачи.

Вы создали текстовый шаблон T4 с помощью C# в качестве управляющего языка для динамической генерации модуля Visual Basic в проекте Visual Basic. Вы можете использовать C# или Visual Basic в качестве языков управления шаблона, чтобы выпускать любой файл в любой тип проекта.

Примечание

К сожалению, версия Т4, которая поставляется с Visual Studio 2010, не позволяют выпускать файлы, основываясь на текущем типе проекта, по крайней мере, без некоторой дополнительной работы. Возможно, будущая версия T4 значительно упростит это.

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