O T4 już kiedyś wspomniałem, przy okazji Entity Framework. W następnych kilku postach, rozwinę jednak temat. Szablony T4 przydatne są nie tylko osobom implementującym biblioteki programistyczne, ale mają miejsce zastosowania w każdym typie aplikacji.
T4 oznacza Text Template Transformation Toolkit i jest to po prostu szablon do generowania plików tekstowych. Bardzo często stosuje się T4 do wygenerowania klas – np. w Entity Framework. Dlatego też T4 jest tak często wykorzystywany w różnych frameworkach. Generowanie klas to tylko jeden z możliwych scenariuszy. W aplikacji biznesowej, T4 mógłby przedstawiać szablon e-mail’a, który wysyłamy do klientów. Każdy email wygląda tak samo, tzn. ma nagłówek, stopkę itp. a jedyną różnicą jest np. imię i nazwisko. W ten sposób możemy stworzyć szablon, posiadający wszystkie części wspólne dla emaili, a w miejsca, które różnią się, umieszczamy specjalną dyrektywę – dzięki temu dynamicznie będziemy w stanie wstawić zawartość.
Podsumowując, T4 to szablon, posiadający tekst oraz pewne zmienne. Następnie, użytkownik przekazuje wartości tych zmiennych, a silnik T4 wygeneruje końcowy plik tekstowy, wstawiając podane wartości zmiennych do szablonu.
Wyróżniamy dwa typy szablonów. Pierwszy z nich jest generowany na etapie projektowania aplikacji (design time), czyli np. przed kompilacją. Przydatny jest dla framework’ow lub innych narzędzi programistycznych, ponieważ wygenerowany tekst, może być następnie wykorzystany w aplikacji. Jeśli dany szablon T4 generuje klasy C# (np. encję), to mogą one potem być wykorzystane w naszym projekcie.
Drugi typ szablonów to oczywiście te generowane podczas działania już aplikacji. Przydatne są w aplikacjach biznesowych, gdzie np. chcemy wysłać wspomniany e-mail. Możemy np. przekazać dane z formularza do T4 i wygenerować potem jakiś plik tekstowy.
Od Visual Studio 2010, T4 jest standardowo dodany do VS i nie musimy nic instalować. Wystarczy przejść do Add->New Item:

Do dyspozycji mamy dwa typy szablonów. Najpierw stwórzmy po prostu “Text Template”, który jest generowany przez Visual Studio. Nowy plik będzie zawierał kilka domyślnych dyrektyw:
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".txt" #>
Pierwsza dyrektywa importuje bibliotekę. Jeśli chcielibyśmy korzystać z jakiś innych klas, niż te w System.Core, będziemy musieli zaimportować w podobny sposób nasz dll. Następne dyrektywy są tym samym, czym jest słowo kluczowe using w C# – po prostu będziemy mogli korzystać bezpośrednio z klas znajdujących się w System.Linq czy System.Text, bez poprzedzania ich namespace.
Ostatnia dyrektywa, określa rozszerzenie wygenerowanego pliku – w tym przypadku jest to .txt. W solution explorer, zobaczymy pod szablonem, wygenerowany plik:

Stwórzmy pierwszy szablon, wyświetlający po prostu tekst:
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".txt" #>
Hello World
Nic ciekawego, ale na wyjściu pojawi się po prostu tekst. Oczywiście, nie ma sensu tworzyć szablonu, który nie ma żadnych elementów dynamicznych. Jeśli tworzymy szablon to prawdopodobnie chcemy przekazać jakieś zmienne lub wygenerować treść w pętli. W każdym T4, możemy korzystać z czystego kodu c# tzn. klas, pętli itp.
Przyjrzymy się teraz Runtime Template. Domyślnie wygenerowany szablon, na początku wygląda bardzo podobnie:
<#@ template language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
Dodajmy jakiś tekst, tak jak w poprzednim przykładzie. Wygenerowany plik, nie jest jednak plikiem tekstowym jak w poprzednim przykładzie, a zawsze klasą c#:

Wygenerowana klasa wygląda następująco:
public partial class RuntimeTextTemplate1 : RuntimeTextTemplate1Base
{
#line hidden
/// <summary>
/// Create the template output
/// </summary>
public virtual string TransformText()
{
this.Write("\r\nHello World!");
return this.GenerationEnvironment.ToString();
}
}
W naszym przypadku, wyświetla ona tylko tekst. Możemy ją wykorzystać potem w naszej aplikacji:
private static void Main(string[] args)
{
var template = new RuntimeTextTemplate1();
Console.WriteLine(template.TransformText());
}
W kolejnych postach zajmiemy się blokami, które możemy umieścić w T4. Po tym poście powinny być jednak zrozumiałe scenariusze, w których możemy zastosować T4.