Нет, это не про Терминатора :)
Это про использование 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();
} #>
Вкратце, по шагам:
- Перебираем все классы из сборки, из нужного пространства имен.
- Для каждого класса генерируем статический класс с таким-же именем и суффиксом Ex.
- Перебираем все свойства класса.
- Генерируем экстеншен-метод нужного типа с именем как оригинальное ствойство, но с префиксом _.
- Для генерация имени нужного типа используется маленькая хитрость. Так как по простому дженерики будут выводиться в 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
Полезные ссылки: