.NET на практике

Ежедневное использование .NET

Кодогенерация T4

с 3 комментариями

Нет, это не про Терминатора :)

Это про использование T4 Text Template Transformation Toolkit, встроенного в Visual Studio 2008 для автоматической генерации кода.

Кратко задача – есть исходники. Опять-то таки сгенерированные, но другим инструментом, Thrift.
Код на C#, публичные поля и свойства в классах с одинаковым именем, только различаются регистром.
Код необходимо использовать из VB.NET. Упс! VB.NET нечувствителен к регистру! Приплыли.

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

Но у нас же есть extension методы – спасибо .NET 3.5! Можно понаписать методов (эх… пока только методы, эктеншен свойств нет) с названием совпадающим со свойствами, но с каким-нибудь префиксом, подчеркиванием например.

Ок, хорошо. Но вручную писать обертки на 20 классов?! Да они еще, как я сказал, могут обновиться в будущем. Тут нужна автоматизация… И в VS 2008 она уже встроена – движок кодогенерации T4.

Файлы с расширением tt. Синтаксис очень похож на ASP.NET, только исполняются внутри Visual Studio (и не только, хостом может выступать любое приложение, и ваше в том числе).

И вот всё волшебство:

<#@ template language=“C#v3.5″ hostspecific=“true” #>
<#@ assembly name=“EDAM.dll” #>
<#@ assembly name=“System.Core” #>
<#@ import namespace=“System.CodeDom” #>
<#@ import namespace=“System.CodeDom.Compiler” #>
<#@ import namespace=“System.IO” #>
<#@ import namespace=“System.Linq” #>
<#@ import namespace=“System.Reflection” #>
<#@ import namespace=“Evernote.EDAM.Type” #>
 
// Autogenerated by <#= Host.GetType() #>
// <#= DateTime.Now #>
// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING

namespace Evernote.EDAM.Type
{
<# foreach (var @class in Assembly.GetAssembly(BaseClass)
              .GetExportedTypes()
              .Where(type => !type.IsNested)
              .Where(type => type.Namespace == BaseClass.Namespace)) { #>
  public static class <#= @class.Name #>Ex
  {
  <# foreach (var prop in @class.GetProperties()) { #>
    public static <#= PrintType(prop.PropertyType) #> _<#= prop.Name #>(this <#= prop.DeclaringType #> value)
    {
      return value.<#= prop.Name #>;
    }
  <# }#>
  }
<# } #>
}
<#+ Type BaseClass = typeof(Note); #>
<#+ 
public string PrintType(Type type)
{
  var typeExpr = new CodeTypeReferenceExpression(type);
  var csProvider = Microsoft.CSharp.CSharpCodeProvider.CreateProvider(“C#”);
  var writer = new StringWriter();
  csProvider.GenerateCodeFromExpression(typeExpr, writer, new CodeGeneratorOptions());
  return writer.ToString();
} #>

 

Вкратце, по шагам:

  1. Перебираем все классы из сборки, из нужного пространства имен.
  2. Для каждого класса генерируем статический класс с таким-же именем и суффиксом Ex.
  3. Перебираем все свойства класса.
  4. Генерируем экстеншен-метод нужного типа с именем как оригинальное ствойство, но с префиксом _.
  5. Для генерация имени нужного типа используется маленькая хитрость. Так как по простому дженерики будут выводиться в IL-нотации, т.е. например System.Generic.List’1[System.String]. И это не будет компилироваться. Надо System.Generic.List<System.String> (для C#). Что и делается через CodeDom. Так как сборка на C# – то и провайдер для C# используется. Можно генерировать и в VB. Как в CodeDom, так и в самом T4 кстати.

Всё, теперь при сборке будет генерироваться набор расширений для каждого нужного класса с методами дублирующими все свойства, которые уже без проблем можно использовать в VB.NET. Кстати, в VB можно опускать скобки при вызове метода, если он не принимает параметров, что делает код еще более изящным (насколько вообще можно говорить об изящности в этой ситуации):

C# note.Attributes.Lattitude
VB.NET note._Attributes._Lattitude

 

Полезные ссылки:

Написано der Igel

Июнь 13, 2009 в 20:56

Опубликовано в Development

Отмечено как , , , , , , ,

Комментариев: 3

Подписаться на комментарии по RSS.

  1. Как часто такая задача возникает и что это за проекты, если не секрет? :) Мне как-то сомнительной кажется необходимость перепрыгивать из C# в VB. Ладно еще, если бы речь про Smalltalk речь шла, или Eiffel…

    terR0Q

    Ноябрь 9, 2009 в 10:21

    • Проекты для веба.
      Задачи довольны часты для всяких заготовок темплейтов вывода бизнес-моделей.
      VB.NET удобен для xml-темплейтов из-за XML Literals прямо в коде (особенно в комбинации с LINQ).

      der Igel

      Ноябрь 9, 2009 в 10:47

      • А, об этом не подумал. Ну если проект заточен на работу с xml, или речь об отдельных модулях с такой же задачей (и в отдельных сборках), то это удобно. А вот весь проект под VB только ради легкости инлайн-разметки я бы подводить не стал.

        terr0q

        Ноябрь 9, 2009 в 11:39


Добавить комментарий

Fill in your details below or click an icon to log in:

Логотип WordPress.com

You are commenting using your WordPress.com account. Log Out / Изменить )

Фотография Twitter

You are commenting using your Twitter account. Log Out / Изменить )

Фотография Facebook

You are commenting using your Facebook account. Log Out / Изменить )

Connecting to %s

Follow

Get every new post delivered to your Inbox.